diff --git a/packages/cli/src/project/WorkspaceLoader.test.ts b/packages/cli/src/project/WorkspaceLoader.test.ts index d9004fcd..abb006c8 100644 --- a/packages/cli/src/project/WorkspaceLoader.test.ts +++ b/packages/cli/src/project/WorkspaceLoader.test.ts @@ -56,6 +56,12 @@ describe(WorkspaceLoader.name, () => { loader.jsons.set(deepInnerProjectPath, deepInnerProject.toJSON()); await loader.save(); + fs.writeFileSync(tmpObj.name + "/demo-project/inner/package.json", "{}"); + fs.writeFileSync( + tmpObj.name + "/demo-project/inner/inner/package.json", + "{}" + ); + expect( loader.projectPathForFile(tmpObj.name + "/demo-project/src/page1.uimix") ).toEqual(tmpObj.name + "/demo-project"); @@ -113,6 +119,9 @@ describe(WorkspaceLoader.name, () => { path.resolve(loader.rootPath, "inner"), innerProject.toJSON() ); + await loader.save(); + + fs.writeFileSync(tmpObj.name + "/demo-project/inner/package.json", "{}"); let watchCount = 0; loader.watch(() => { diff --git a/packages/cli/src/project/WorkspaceLoader.ts b/packages/cli/src/project/WorkspaceLoader.ts index 5cfd87b2..29a5cb18 100644 --- a/packages/cli/src/project/WorkspaceLoader.ts +++ b/packages/cli/src/project/WorkspaceLoader.ts @@ -71,7 +71,8 @@ export class WorkspaceLoader { } readonly filePattern: string; - readonly manifestName = "uimix.json"; + readonly uimixProjectFile = "uimix.json"; + readonly projectBoundary = "package.json"; // TODO: other project boundaries jsons = new Map(); // project path -> project json get json(): ProjectJSON { @@ -97,7 +98,7 @@ export class WorkspaceLoader { async load(): Promise { const filePaths = await this.fileAccess.glob( - `{${this.filePattern},**/${this.manifestName}}` + `{${this.filePattern},**/${this.projectBoundary}}` ); filePaths.sort(); @@ -106,14 +107,14 @@ export class WorkspaceLoader { ]); for (const manifestPaths of filePaths.filter((filePath) => - filePath.endsWith(this.manifestName) + filePath.endsWith(this.projectBoundary) )) { const parentPath = path.dirname(manifestPaths); filePathsForProject.set(parentPath, []); } for (const filePath of filePaths) { - if (filePath.endsWith(this.manifestName)) { + if (filePath.endsWith(this.projectBoundary)) { continue; } @@ -134,7 +135,7 @@ export class WorkspaceLoader { manifest = ProjectManifestJSON.parse( JSON.parse( await this.fileAccess.readText( - path.join(projectPath, this.manifestName) + path.join(projectPath, this.uimixProjectFile) ) ) ); @@ -145,9 +146,14 @@ export class WorkspaceLoader { const pages = new Map(); for (const pagePath of pagePaths) { - const pageJSON = PageJSON.parse( - JSON.parse(await this.fileAccess.readText(pagePath)) - ); + const pageText = await this.fileAccess.readText(pagePath); + + const pageJSON = + pageText.trim() === "" + ? { nodes: {}, styles: {} } + : PageJSON.parse( + JSON.parse(await this.fileAccess.readText(pagePath)) + ); pages.set( path.relative(projectPath, pagePath).replace(/\.uimix$/, ""), @@ -183,14 +189,8 @@ export class WorkspaceLoader { for (const [projectPath, json] of this.jsons) { const { manifest, pages } = projectJSONToFiles(json); - if (pages.size === 0) { - continue; - } - await this.fileAccess.writeText( - path.join(projectPath, this.manifestName), - formatJSON(JSON.stringify(manifest)) - ); + let projectSaved = false; for (const [pageName, pageJSON] of pages) { const pagePath = path.join(projectPath, pageName + ".uimix"); @@ -202,6 +202,14 @@ export class WorkspaceLoader { formatJSON(JSON.stringify(pageJSON)) ); pagePathsToDelete.delete(pagePath); + projectSaved = true; + } + + if (projectSaved) { + await this.fileAccess.writeText( + path.join(projectPath, this.uimixProjectFile), + formatJSON(JSON.stringify(manifest)) + ); } } @@ -216,18 +224,21 @@ export class WorkspaceLoader { watch(onChange: (projectJSON: ProjectJSON) => void): () => void { console.log("start watching..."); - return this.fileAccess.watch(this.filePattern, async () => { - try { - if (this.isSaving) { - return; - } - if (await this.load()) { - onChange(this.json); + return this.fileAccess.watch( + `{${this.filePattern},**/${this.projectBoundary}}`, + async () => { + try { + if (this.isSaving) { + return; + } + if (await this.load()) { + onChange(this.json); + } + } catch (e) { + console.error(e); } - } catch (e) { - console.error(e); } - }); + ); } } diff --git a/packages/editor/src/state/ProjectState.ts b/packages/editor/src/state/ProjectState.ts index 322c7202..d29c9275 100644 --- a/packages/editor/src/state/ProjectState.ts +++ b/packages/editor/src/state/ProjectState.ts @@ -17,7 +17,7 @@ import { getIncrementalUniqueName } from "@uimix/foundation/src/utils/Name"; import { PageState } from "./PageState"; import { ScrollState } from "./ScrollState"; // eslint-disable-next-line import/no-unresolved -import demoFile from "./demo.uimix?raw"; +import demoFile from "./demoFile/demo.uimix?raw"; import { filesToProjectJSON } from "../../../cli/src/project/WorkspaceLoader"; export class ProjectState { diff --git a/packages/editor/src/state/demo.uimix b/packages/editor/src/state/demoFile/demo.uimix similarity index 100% rename from packages/editor/src/state/demo.uimix rename to packages/editor/src/state/demoFile/demo.uimix diff --git a/packages/editor/src/state/demoFile/package.json b/packages/editor/src/state/demoFile/package.json new file mode 100644 index 00000000..0967ef42 --- /dev/null +++ b/packages/editor/src/state/demoFile/package.json @@ -0,0 +1 @@ +{} diff --git a/packages/editor/src/state/uimix.json b/packages/editor/src/state/uimix.json deleted file mode 100644 index 83680f2b..00000000 --- a/packages/editor/src/state/uimix.json +++ /dev/null @@ -1 +0,0 @@ -{ "componentURLs": [] } diff --git a/packages/sandbox/src/nested/README.md b/packages/sandbox/src/nested/README.md new file mode 100644 index 00000000..c26c4111 --- /dev/null +++ b/packages/sandbox/src/nested/README.md @@ -0,0 +1,5 @@ +## Nested project example + +UIMix identifies any directory containing a package.json file as a project. + +It allows for the nesting of projects within one another, while ensuring that files from the nested project are not included in the parent project. This feature is useful when setting up a monorepo. diff --git a/packages/sandbox/src/nested/package.json b/packages/sandbox/src/nested/package.json new file mode 100644 index 00000000..0967ef42 --- /dev/null +++ b/packages/sandbox/src/nested/package.json @@ -0,0 +1 @@ +{}