Skip to content

Commit

Permalink
Replace parcel watcher with chokidar (#5932)
Browse files Browse the repository at this point in the history
  • Loading branch information
AlexandreSi authored Nov 22, 2023
1 parent 65b78d4 commit 1ac248b
Show file tree
Hide file tree
Showing 11 changed files with 321 additions and 6,066 deletions.
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);

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

0 comments on commit 1ac248b

Please sign in to comment.