Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace parcel watcher with chokidar #5932

Merged
merged 1 commit into from
Nov 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions newIDE/app/src/EventsSheet/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,10 @@ import { type Tutorial } from '../Utils/GDevelopServices/Tutorial';
import AlertMessage from '../UI/AlertMessage';
import { Column, Line } from '../UI/Grid';
import ErrorBoundary from '../UI/ErrorBoundary';
import {
registerOnResourceExternallyChangedCallback,
unregisterOnResourceExternallyChangedCallback,
} from '../MainFrame/ResourcesWatcher';

const gd: libGDevelop = global.gd;

Expand Down Expand Up @@ -238,6 +242,7 @@ export class EventsSheetComponentWithoutHandle extends React.Component<
});

eventContextMenu: ?ContextMenuInterface;
resourceExternallyChangedCallbackId: ?string;
instructionContextMenu: ?ContextMenuInterface;
addNewEvent: (
type: string,
Expand Down Expand Up @@ -297,6 +302,14 @@ export class EventsSheetComponentWithoutHandle extends React.Component<

componentDidMount() {
this.setState({ allEventsMetadata: enumerateEventsMetadata() });
this.resourceExternallyChangedCallbackId = registerOnResourceExternallyChangedCallback(
this.onResourceExternallyChanged.bind(this)
);
}
componentWillUnmount() {
unregisterOnResourceExternallyChangedCallback(
this.resourceExternallyChangedCallbackId
);
}

componentDidUpdate(prevProps: ComponentProps, prevState: State) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ import { sendEventsExtractedAsFunction } from '../../Utils/Analytics/EventSender
import HelpButton from '../../UI/HelpButton';
import TutorialButton from '../../UI/TutorialButton';
import EditSceneIcon from '../../UI/CustomSvgIcons/EditScene';
import {
registerOnResourceExternallyChangedCallback,
unregisterOnResourceExternallyChangedCallback,
} from '../ResourcesWatcher';

const styles = {
container: {
Expand All @@ -36,6 +40,7 @@ export class ExternalEventsEditorContainer extends React.Component<
State
> {
editor: ?EventsSheetInterface;
resourceExternallyChangedCallbackId: ?string;

state = {
externalPropertiesDialogOpen: false,
Expand All @@ -48,6 +53,17 @@ export class ExternalEventsEditorContainer extends React.Component<
return this.props.isActive || nextProps.isActive;
}

componentDidMount() {
this.resourceExternallyChangedCallbackId = registerOnResourceExternallyChangedCallback(
this.onResourceExternallyChanged.bind(this)
);
}
componentWillUnmount() {
unregisterOnResourceExternallyChangedCallback(
this.resourceExternallyChangedCallbackId
);
}

onResourceExternallyChanged = (resourceInfo: {| identifier: string |}) => {
if (this.editor) this.editor.onResourceExternallyChanged(resourceInfo);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ import Text from '../../UI/Text';
import { prepareInstancesEditorSettings } from '../../InstancesEditor/InstancesEditorSettings';
import TutorialButton from '../../UI/TutorialButton';
import HelpButton from '../../UI/HelpButton';
import {
registerOnResourceExternallyChangedCallback,
unregisterOnResourceExternallyChangedCallback,
} from '../ResourcesWatcher';

const styles = {
container: {
Expand All @@ -38,6 +42,7 @@ export class ExternalLayoutEditorContainer extends React.Component<
State
> {
editor: ?SceneEditor;
resourceExternallyChangedCallbackId: ?string;
state = {
externalPropertiesDialogOpen: false,
};
Expand All @@ -63,6 +68,14 @@ export class ExternalLayoutEditorContainer extends React.Component<
projectItemName
);
}
this.resourceExternallyChangedCallbackId = registerOnResourceExternallyChangedCallback(
this.onResourceExternallyChanged.bind(this)
);
}
componentWillUnmount() {
unregisterOnResourceExternallyChangedCallback(
this.resourceExternallyChangedCallbackId
);
}

componentDidUpdate(prevProps: RenderEditorContainerProps) {
Expand Down
48 changes: 26 additions & 22 deletions newIDE/app/src/MainFrame/ResourcesWatcher.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,36 @@ import * as React from 'react';
import ResourcesLoader from '../ResourcesLoader';
import PreferencesContext from './Preferences/PreferencesContext';
import { type StorageProvider, type FileMetadata } from '../ProjectsStorage';
import { type EditorTabsState } from './EditorTabs/EditorTabsHandler';

const callbacks: { [key: string]: Function } = {};

let callbackId = 1;
const getNewId = () => {
return callbackId++;
};

export const registerOnResourceExternallyChangedCallback = (
callback: Function
) => {
const id = getNewId().toString();
callbacks[id] = callback;
return id;
};

export const unregisterOnResourceExternallyChangedCallback = (
callbackId: ?string
) => {
if (!callbackId || !callbacks[callbackId]) return;
delete callbacks[callbackId];
};

const useResourcesWatcher = ({
getStorageProvider,
fileMetadata,
editorTabs,
isProjectSplitInMultipleFiles,
}: {|
getStorageProvider: () => StorageProvider,
fileMetadata: ?FileMetadata,
editorTabs: EditorTabsState,
isProjectSplitInMultipleFiles: boolean,
|}) => {
const {
Expand All @@ -23,29 +42,14 @@ const useResourcesWatcher = ({
// thus triggering the effect.
const fileIdentifier = fileMetadata ? fileMetadata.fileIdentifier : null;

// Callbacks are extracted from editorTabs to avoid redefining informEditorsResourceExternallyChanged
// thus triggering the effect on each active tab change (stored in editorTabs).
const callbacks = React.useMemo(
() =>
editorTabs.editors
.map(
editorTab =>
editorTab.editorRef &&
editorTab.editorRef.editor &&
// Each editor container has an accessible editor property.
// $FlowFixMe[prop-missing]
editorTab.editorRef.editor.onResourceExternallyChanged
)
.filter(Boolean),
[editorTabs.editors]
);

const informEditorsResourceExternallyChanged = React.useCallback(
(resourceInfo: {| identifier: string |}) => {
ResourcesLoader.burstAllUrlsCache();
callbacks.forEach(callback => callback(resourceInfo));
Object.keys(callbacks).forEach(callbackId =>
callbacks[callbackId](resourceInfo)
);
},
[callbacks]
[]
);

React.useEffect(
Expand Down
1 change: 0 additions & 1 deletion newIDE/app/src/MainFrame/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -504,7 +504,6 @@ const MainFrame = (props: Props) => {
useResourcesWatcher({
getStorageProvider,
fileMetadata: currentFileMetadata,
editorTabs: state.editorTabs,
isProjectSplitInMultipleFiles: currentProject
? currentProject.isFolderProject()
: false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,11 @@ export const setupResourcesWatcher =
path.join(folderPath, autosaveFile),
];
if (options && options.isProjectSplitInMultipleFiles) {
ignore.push(`{${splittedProjectFolderNames.join(',')}}/*.json`);
ignore.push(
...splittedProjectFolderNames.map(folderName =>
path.join(folderPath, folderName, '*.json')
)
);
}
const subscriptionIdPromise = ipcRenderer.invoke(
'local-filesystem-watcher-setup',
Expand Down
17 changes: 16 additions & 1 deletion newIDE/app/src/ResourcesEditor/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ import {
import { type FileMetadata } from '../ProjectsStorage';
import { getResourceFilePathStatus } from '../ResourcesList/ResourceUtils';
import type { StorageProvider } from '../ProjectsStorage';
import {
registerOnResourceExternallyChangedCallback,
unregisterOnResourceExternallyChangedCallback,
} from '../MainFrame/ResourcesWatcher';

const gd: libGDevelop = global.gd;

Expand Down Expand Up @@ -65,7 +69,7 @@ export default class ResourcesEditor extends React.Component<Props, State> {
static defaultProps = {
setToolbar: () => {},
};

resourceExternallyChangedCallbackId: ?string;
editorMosaic: ?EditorMosaic = null;
_propertiesEditor: ?ResourcePropertiesEditorInterface = null;
_resourcesList: ?ResourcesList = null;
Expand All @@ -74,6 +78,17 @@ export default class ResourcesEditor extends React.Component<Props, State> {
selectedResource: null,
};

componentDidMount() {
this.resourceExternallyChangedCallbackId = registerOnResourceExternallyChangedCallback(
this.onResourceExternallyChanged.bind(this)
);
}
componentWillUnmount() {
unregisterOnResourceExternallyChangedCallback(
this.resourceExternallyChangedCallbackId
);
}

refreshResourcesList() {
if (this._resourcesList) this._resourcesList.forceUpdate();
}
Expand Down
16 changes: 16 additions & 0 deletions newIDE/app/src/SceneEditor/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,10 @@ import {
} from '../ObjectsList/EnumerateObjectFolderOrObject';
import uniq from 'lodash/uniq';
import { cleanNonExistingObjectFolderOrObjectWithContexts } from './ObjectFolderOrObjectsSelection';
import {
registerOnResourceExternallyChangedCallback,
unregisterOnResourceExternallyChangedCallback,
} from '../MainFrame/ResourcesWatcher';

const gd: libGDevelop = global.gd;

Expand Down Expand Up @@ -153,6 +157,7 @@ export default class SceneEditor extends React.Component<Props, State> {
instancesSelection: InstancesSelection;
contextMenu: ?ContextMenuInterface;
editorDisplay: ?SceneEditorsDisplayInterface;
resourceExternallyChangedCallbackId: ?string;

constructor(props: Props) {
super(props);
Expand Down Expand Up @@ -199,6 +204,17 @@ export default class SceneEditor extends React.Component<Props, State> {
this.props.unsavedChanges.triggerUnsavedChanges();
}

componentDidMount() {
this.resourceExternallyChangedCallbackId = registerOnResourceExternallyChangedCallback(
this.onResourceExternallyChanged.bind(this)
);
}
componentWillUnmount() {
unregisterOnResourceExternallyChangedCallback(
this.resourceExternallyChangedCallbackId
);
}

getInstancesEditorSettings() {
return this.state.instancesEditorSettings;
}
Expand Down
45 changes: 26 additions & 19 deletions newIDE/electron-app/app/LocalFilesystemWatcher.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
// @flow
const watcher = require('@parcel/watcher');
const fileWatcher = require('chokidar');

let subscriptions = {};
let newSubscriptionId = 1
let subscriptionCancelers = {};
let newSubscriptionId = 1;

const getNewSubscriptionId = () => {
const id = newSubscriptionId++;
Expand All @@ -12,31 +12,38 @@ const getNewSubscriptionId = () => {
const setupWatcher = (folderPath, fileWiseCallback, serializedOptions) => {
const options = JSON.parse(serializedOptions);
const newSubscriptionId = getNewSubscriptionId();
watcher
.subscribe(
folderPath,
(err, fileChangeEvents) => {
fileChangeEvents.forEach(fileChangeEvent =>
fileWiseCallback(fileChangeEvent.path)
);
const watcher = fileWatcher
.watch(folderPath, {
ignored: options.ignore,
ignoreInitial: true,
awaitWriteFinish: {
stabilityThreshold: 250,
pollInterval: 100,
},
options
})
.on(
'change',
// TODO: Is it safe to let it like that since the OS could for some reason
// do never-ending operations on the folder or its children, making the debounce
// never ending.
fileWiseCallback
)
.then(subscription => {
subscriptions[newSubscriptionId] = subscription;
});
.on('unlink', fileWiseCallback)
.on('add', fileWiseCallback);

subscriptionCancelers[newSubscriptionId] = () => watcher.unwatch(folderPath);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just to check, this function is not async?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope!


return newSubscriptionId;
};

const disableWatcher = id => {
const subscription = subscriptions[id];
if (!subscription) {
const subscriptionCanceler = subscriptionCancelers[id];
if (!subscriptionCanceler) {
console.log('No watcher subscription to disable.');
return;
}
subscription.unsubscribe().then(() => {
delete subscriptions[id];
});
subscriptionCanceler();
delete subscriptionCancelers[id];
};

module.exports = {
Expand Down
Loading