@jakubmazanec/carson 0.2.0 → 0.3.0-next.1b2cc9c

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/README.md +11 -11
  2. package/dist/constants.d.ts +2 -1
  3. package/dist/constants.js +2 -1
  4. package/dist/constants.js.map +1 -1
  5. package/dist/template/applyTemplateRenders.d.ts +3 -2
  6. package/dist/template/applyTemplateRenders.js +5 -6
  7. package/dist/template/applyTemplateRenders.js.map +1 -1
  8. package/dist/template/internals/createUsingTemplateRender.js +49 -4
  9. package/dist/template/internals/createUsingTemplateRender.js.map +1 -1
  10. package/dist/template/internals/mergeUsingTemplateRender.js +26 -14
  11. package/dist/template/internals/mergeUsingTemplateRender.js.map +1 -1
  12. package/dist/workspace/Project.js +19 -14
  13. package/dist/workspace/Project.js.map +1 -1
  14. package/dist/workspace/Workspace.d.ts +3 -0
  15. package/dist/workspace/Workspace.js +29 -19
  16. package/dist/workspace/Workspace.js.map +1 -1
  17. package/dist/workspace/internals/containsProject.js +9 -4
  18. package/dist/workspace/internals/containsProject.js.map +1 -1
  19. package/dist/workspace/internals/containsWorkspace.d.ts +1 -0
  20. package/dist/workspace/internals/containsWorkspace.js +14 -0
  21. package/dist/workspace/internals/containsWorkspace.js.map +1 -0
  22. package/dist/workspace/internals.d.ts +1 -0
  23. package/dist/workspace/internals.js +1 -0
  24. package/dist/workspace/internals.js.map +1 -1
  25. package/package.json +2 -2
  26. package/src/constants.ts +2 -1
  27. package/src/template/applyTemplateRenders.ts +8 -12
  28. package/src/template/internals/createUsingTemplateRender.ts +42 -2
  29. package/src/template/internals/mergeUsingTemplateRender.ts +17 -9
  30. package/src/workspace/Project.ts +21 -12
  31. package/src/workspace/Workspace.ts +39 -22
  32. package/src/workspace/internals/containsProject.ts +19 -4
  33. package/src/workspace/internals/containsWorkspace.ts +24 -0
  34. package/src/workspace/internals.ts +1 -0
package/README.md CHANGED
@@ -73,19 +73,19 @@ Take a look at this simplified example of a workspace:
73
73
  ```
74
74
  workspace/
75
75
  ├── .carson/
76
- │ ├── .snapshots
76
+ │ ├── workspace.snapshot
77
77
  │ └── workspace.json
78
78
  ├── packages/
79
79
  │ ├── foo/
80
80
  │ │ ├── .carson/
81
- │ │ │ ├── .snapshots
81
+ │ │ │ ├── project.snapshot
82
82
  │ │ │ └── project.json
83
83
  │ │ ├── src/
84
84
  │ │ │ └── main.js
85
85
  │ │ └── package.json
86
86
  │ └── bar/
87
87
  │ ├── .carson/
88
- │ │ ├── .snapshots
88
+ │ │ ├── project.snapshot
89
89
  │ │ └── project.json
90
90
  │ ├── src/
91
91
  │ │ └── main.js
@@ -329,14 +329,14 @@ file format detection by Prettier.
329
329
  Attribute `strategy` describes how exactly should the template render be written, or whether it
330
330
  should be written at all.
331
331
 
332
- | Strategy name | Example `strategy` value | Description |
333
- | ------------- | ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
334
- | Create | `'create'` | The template render is written to the target file, but only when the Carson template is used to create a new workspace or project; when Carson performs an update, the template files with this strategy are not even rendered. |
335
- | Ensure | `'ensure'` | The template render is written to the target file, but only when the target file doesn't exist; if it does, it is left unchanged. |
336
- | Overwrite | `'overwrite'` | The template render is written to the target file, overwriting its existing content. |
337
- | Merge | `'merge'` | The template render is merged with the target file's existing content. Only simple objects, not arrays, are merged recursively. The only supported file formats are JSON and YAML. |
338
- | Check | `'check'` | Only the target file's existence is checked, the template render is discarded; when Carson doesn't perform an update, the template files with this strategy are not even rendered. |
339
- | Insert | `['insert', <string>]` | The template render is inserted between the first and last occurrence of an HTML comment containing configured string, e.g. `<!-- foobar -->`. The only supported file format is Markdown. |
332
+ | Strategy name | Example `strategy` value | Description |
333
+ | ------------- | ------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
334
+ | Create | `'create'` | The template render is written to the target file, but only when the Carson template is used to create a new workspace or project; when Carson performs an update, the template files with this strategy are not even rendered. If the target file already exists and its format is JSON or YAML, the template render is merged with the target file's existing content (otherwise it's overwritten). Only simple objects, not arrays, are merged recursively. |
335
+ | Ensure | `'ensure'` | The template render is written to the target file, but only when the target file doesn't exist; if it does, it is left unchanged. |
336
+ | Overwrite | `'overwrite'` | The template render is written to the target file, overwriting its existing content. |
337
+ | Merge | `'merge'` | The template render is merged with the target file's existing content. Only simple objects, not arrays, are merged recursively. The only supported file formats are JSON and YAML. If the target file doesn't exist, it is created. |
338
+ | Check | `'check'` | Only the target file's existence is checked, the template render is discarded; when Carson doesn't perform an update, the template files with this strategy are not even rendered. |
339
+ | Insert | `['insert', <string>]` | The template render is inserted between the first and last occurrence of an HTML comment containing configured string, e.g. `<!-- foobar -->`. The only supported file format is Markdown. |
340
340
 
341
341
  The rest of the template file after the front matter is the actual EJS template. Please read
342
342
  [`ejs` documentation](https://ejs.co/#docs) to find out in more detail how they work.
@@ -2,7 +2,8 @@ export declare const PACKAGE_PATH: string;
2
2
  export declare const CARSON_CONFIG_DIRECTORY = ".carson";
3
3
  export declare const PROJECT_CONFIG_FILENAME = "project.json";
4
4
  export declare const WORKSPACE_CONFIG_FILENAME = "workspace.json";
5
- export declare const SNAPSHOTS_FILENAME = ".snapshots";
5
+ export declare const PROJECT_SNAPSHOT_FILENAME = "project.snapshot";
6
+ export declare const WORKSPACE_SNAPSHOT_FILENAME = "workspace.snapshot";
6
7
  export declare const TEMPLATES_PATH = "templates";
7
8
  export declare const TEMPLATE_CONFIG_FILENAME = "config.json";
8
9
  export declare const PACKAGE_JSON_FILENAME = "package.json";
package/dist/constants.js CHANGED
@@ -5,7 +5,8 @@ export const PACKAGE_PATH = path.resolve(directory, '..');
5
5
  export const CARSON_CONFIG_DIRECTORY = '.carson';
6
6
  export const PROJECT_CONFIG_FILENAME = 'project.json';
7
7
  export const WORKSPACE_CONFIG_FILENAME = 'workspace.json';
8
- export const SNAPSHOTS_FILENAME = '.snapshots';
8
+ export const PROJECT_SNAPSHOT_FILENAME = 'project.snapshot';
9
+ export const WORKSPACE_SNAPSHOT_FILENAME = 'workspace.snapshot';
9
10
  export const TEMPLATES_PATH = 'templates';
10
11
  export const TEMPLATE_CONFIG_FILENAME = 'config.json';
11
12
  export const PACKAGE_JSON_FILENAME = 'package.json';
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/constants.ts"],"sourcesContent":["import path from 'node:path';\nimport url from 'node:url';\n\nlet directory = path.dirname(url.fileURLToPath(import.meta.url));\n\nexport const PACKAGE_PATH = path.resolve(directory, '..');\n\nexport const CARSON_CONFIG_DIRECTORY = '.carson';\nexport const PROJECT_CONFIG_FILENAME = 'project.json';\nexport const WORKSPACE_CONFIG_FILENAME = 'workspace.json';\nexport const SNAPSHOTS_FILENAME = '.snapshots';\nexport const TEMPLATES_PATH = 'templates';\nexport const TEMPLATE_CONFIG_FILENAME = 'config.json';\nexport const PACKAGE_JSON_FILENAME = 'package.json';\n"],"names":["path","url","directory","dirname","fileURLToPath","PACKAGE_PATH","resolve","CARSON_CONFIG_DIRECTORY","PROJECT_CONFIG_FILENAME","WORKSPACE_CONFIG_FILENAME","SNAPSHOTS_FILENAME","TEMPLATES_PATH","TEMPLATE_CONFIG_FILENAME","PACKAGE_JSON_FILENAME"],"mappings":"AAAA,OAAOA,UAAU,YAAY;AAC7B,OAAOC,SAAS,WAAW;AAE3B,IAAIC,YAAYF,KAAKG,OAAO,CAACF,IAAIG,aAAa,CAAC,YAAYH,GAAG;AAE9D,OAAO,MAAMI,eAAeL,KAAKM,OAAO,CAACJ,WAAW,MAAM;AAE1D,OAAO,MAAMK,0BAA0B,UAAU;AACjD,OAAO,MAAMC,0BAA0B,eAAe;AACtD,OAAO,MAAMC,4BAA4B,iBAAiB;AAC1D,OAAO,MAAMC,qBAAqB,aAAa;AAC/C,OAAO,MAAMC,iBAAiB,YAAY;AAC1C,OAAO,MAAMC,2BAA2B,cAAc;AACtD,OAAO,MAAMC,wBAAwB,eAAe"}
1
+ {"version":3,"sources":["../src/constants.ts"],"sourcesContent":["import path from 'node:path';\nimport url from 'node:url';\n\nlet directory = path.dirname(url.fileURLToPath(import.meta.url));\n\nexport const PACKAGE_PATH = path.resolve(directory, '..');\n\nexport const CARSON_CONFIG_DIRECTORY = '.carson';\nexport const PROJECT_CONFIG_FILENAME = 'project.json';\nexport const WORKSPACE_CONFIG_FILENAME = 'workspace.json';\nexport const PROJECT_SNAPSHOT_FILENAME = 'project.snapshot';\nexport const WORKSPACE_SNAPSHOT_FILENAME = 'workspace.snapshot';\nexport const TEMPLATES_PATH = 'templates';\nexport const TEMPLATE_CONFIG_FILENAME = 'config.json';\nexport const PACKAGE_JSON_FILENAME = 'package.json';\n"],"names":["path","url","directory","dirname","fileURLToPath","PACKAGE_PATH","resolve","CARSON_CONFIG_DIRECTORY","PROJECT_CONFIG_FILENAME","WORKSPACE_CONFIG_FILENAME","PROJECT_SNAPSHOT_FILENAME","WORKSPACE_SNAPSHOT_FILENAME","TEMPLATES_PATH","TEMPLATE_CONFIG_FILENAME","PACKAGE_JSON_FILENAME"],"mappings":"AAAA,OAAOA,UAAU,YAAY;AAC7B,OAAOC,SAAS,WAAW;AAE3B,IAAIC,YAAYF,KAAKG,OAAO,CAACF,IAAIG,aAAa,CAAC,YAAYH,GAAG;AAE9D,OAAO,MAAMI,eAAeL,KAAKM,OAAO,CAACJ,WAAW,MAAM;AAE1D,OAAO,MAAMK,0BAA0B,UAAU;AACjD,OAAO,MAAMC,0BAA0B,eAAe;AACtD,OAAO,MAAMC,4BAA4B,iBAAiB;AAC1D,OAAO,MAAMC,4BAA4B,mBAAmB;AAC5D,OAAO,MAAMC,8BAA8B,qBAAqB;AAChE,OAAO,MAAMC,iBAAiB,YAAY;AAC1C,OAAO,MAAMC,2BAA2B,cAAc;AACtD,OAAO,MAAMC,wBAAwB,eAAe"}
@@ -1,10 +1,11 @@
1
1
  import { type TemplateRenders } from './TemplateRenders.js';
2
2
  export type ApplyTemplateRendersOptions = {
3
3
  templateRenders: TemplateRenders;
4
- path: string;
4
+ targetPath: string;
5
+ snapshotPath: string;
5
6
  ignoreStrategies: string[];
6
7
  };
7
8
  /**
8
9
  * Applies rendered Carson template to a specified path, i.e. creates and updates files based on the result of rendering each Carson template file.
9
10
  */
10
- export declare function applyTemplateRenders({ templateRenders, path: targetPath, ignoreStrategies, }: ApplyTemplateRendersOptions): Promise<void>;
11
+ export declare function applyTemplateRenders({ templateRenders, targetPath, snapshotPath, ignoreStrategies, }: ApplyTemplateRendersOptions): Promise<void>;
@@ -4,16 +4,15 @@ import path from 'node:path';
4
4
  import prettier from 'prettier';
5
5
  import { CarsonTemplateError } from './CarsonTemplateError.js';
6
6
  import { createUsingTemplateRender, deleteUsingTemplateRender, insertUsingTemplateRender, loadTemplateRenderSnapshots, mergeUsingTemplateRender, overwriteUsingTemplateRender, saveTemplateRenderSnapshots, uninsertUsingTemplateRender, unmergeUsingTemplateRender } from './internals.js';
7
- import { CARSON_CONFIG_DIRECTORY, SNAPSHOTS_FILENAME } from '../constants.js';
7
+ import { CARSON_CONFIG_DIRECTORY } from '../constants.js';
8
8
  /**
9
9
  * Applies rendered Carson template to a specified path, i.e. creates and updates files based on the result of rendering each Carson template file.
10
- */ export async function applyTemplateRenders({ templateRenders, path: targetPath, ignoreStrategies }) {
10
+ */ export async function applyTemplateRenders({ templateRenders, targetPath, snapshotPath, ignoreStrategies }) {
11
11
  // we need to clear Prettier cache, because we are changing file system
12
12
  await prettier.clearConfigCache();
13
13
  // first, we read the existing snapshots and use them to undo previous changes
14
- let templateRenderSnapshotsPath = path.join(targetPath, CARSON_CONFIG_DIRECTORY, SNAPSHOTS_FILENAME);
15
- if (await fs.pathExists(templateRenderSnapshotsPath)) {
16
- let templateRenderSnapshots = await loadTemplateRenderSnapshots(templateRenderSnapshotsPath);
14
+ if (await fs.pathExists(snapshotPath)) {
15
+ let templateRenderSnapshots = await loadTemplateRenderSnapshots(snapshotPath);
17
16
  for (let templateRenderSnapshot of templateRenderSnapshots){
18
17
  let strategy = typeof templateRenderSnapshot.attributes.strategy === 'string' ? templateRenderSnapshot.attributes.strategy : templateRenderSnapshot.attributes.strategy[0];
19
18
  if (ignoreStrategies.includes(strategy)) {
@@ -85,7 +84,7 @@ import { CARSON_CONFIG_DIRECTORY, SNAPSHOTS_FILENAME } from '../constants.js';
85
84
  }
86
85
  }
87
86
  await fs.ensureDir(path.join(targetPath, CARSON_CONFIG_DIRECTORY));
88
- await saveTemplateRenderSnapshots(templateRenders, templateRenderSnapshotsPath);
87
+ await saveTemplateRenderSnapshots(templateRenders, snapshotPath);
89
88
  }
90
89
 
91
90
  //# sourceMappingURL=applyTemplateRenders.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/template/applyTemplateRenders.ts"],"sourcesContent":["// TODO: can this be fixed?\n/* eslint-disable complexity -- TODO */\nimport fs from 'fs-extra';\nimport path from 'node:path';\nimport prettier from 'prettier';\n\nimport {CarsonTemplateError} from './CarsonTemplateError.js';\nimport {\n createUsingTemplateRender,\n deleteUsingTemplateRender,\n insertUsingTemplateRender,\n loadTemplateRenderSnapshots,\n mergeUsingTemplateRender,\n overwriteUsingTemplateRender,\n saveTemplateRenderSnapshots,\n uninsertUsingTemplateRender,\n unmergeUsingTemplateRender,\n} from './internals.js';\nimport {type TemplateRenders} from './TemplateRenders.js';\nimport {CARSON_CONFIG_DIRECTORY, SNAPSHOTS_FILENAME} from '../constants.js';\n\nexport type ApplyTemplateRendersOptions = {\n templateRenders: TemplateRenders;\n path: string;\n ignoreStrategies: string[];\n};\n\n/**\n * Applies rendered Carson template to a specified path, i.e. creates and updates files based on the result of rendering each Carson template file.\n */\nexport async function applyTemplateRenders({\n templateRenders,\n path: targetPath,\n ignoreStrategies,\n}: ApplyTemplateRendersOptions) {\n // we need to clear Prettier cache, because we are changing file system\n await prettier.clearConfigCache();\n\n // first, we read the existing snapshots and use them to undo previous changes\n let templateRenderSnapshotsPath = path.join(\n targetPath,\n CARSON_CONFIG_DIRECTORY,\n SNAPSHOTS_FILENAME,\n );\n\n if (await fs.pathExists(templateRenderSnapshotsPath)) {\n let templateRenderSnapshots = await loadTemplateRenderSnapshots(templateRenderSnapshotsPath);\n\n for (let templateRenderSnapshot of templateRenderSnapshots) {\n let strategy =\n typeof templateRenderSnapshot.attributes.strategy === 'string'\n ? templateRenderSnapshot.attributes.strategy\n : templateRenderSnapshot.attributes.strategy[0];\n\n if (ignoreStrategies.includes(strategy)) {\n continue;\n }\n\n if (templateRenderSnapshot.attributes.strategy === 'ensure') {\n await deleteUsingTemplateRender(templateRenderSnapshot, targetPath, {\n onlyDeleteWhenContentsMatch: true,\n });\n }\n\n if (templateRenderSnapshot.attributes.strategy === 'overwrite') {\n await deleteUsingTemplateRender(templateRenderSnapshot, targetPath);\n }\n\n if (templateRenderSnapshot.attributes.strategy === 'merge') {\n await unmergeUsingTemplateRender(templateRenderSnapshot, targetPath);\n }\n\n if (templateRenderSnapshot.attributes.strategy[0] === 'insert') {\n await uninsertUsingTemplateRender(templateRenderSnapshot, targetPath);\n }\n }\n }\n\n // second, we apply strategies that create files\n for (let templateRender of templateRenders) {\n let strategy =\n typeof templateRender.attributes.strategy === 'string'\n ? templateRender.attributes.strategy\n : templateRender.attributes.strategy[0];\n\n if (ignoreStrategies.includes(strategy)) {\n continue;\n }\n\n if (templateRender.attributes.strategy === 'create') {\n await createUsingTemplateRender(templateRender, targetPath);\n }\n\n if (templateRender.attributes.strategy === 'ensure') {\n let to = path.join(targetPath, templateRender.attributes.to);\n let doesToExist = await fs.pathExists(to);\n\n if (!doesToExist) {\n await createUsingTemplateRender(templateRender, targetPath);\n }\n }\n }\n\n // third, we apply strategies that change files\n for (let templateRender of templateRenders) {\n let strategy =\n typeof templateRender.attributes.strategy === 'string'\n ? templateRender.attributes.strategy\n : templateRender.attributes.strategy[0];\n\n if (ignoreStrategies.includes(strategy)) {\n continue;\n }\n\n if (templateRender.attributes.strategy === 'overwrite') {\n await overwriteUsingTemplateRender(templateRender, targetPath);\n }\n\n if (templateRender.attributes.strategy === 'merge') {\n await mergeUsingTemplateRender(templateRender, targetPath);\n }\n\n if (templateRender.attributes.strategy[0] === 'insert') {\n await insertUsingTemplateRender(templateRender, targetPath);\n }\n\n if (templateRender.attributes.strategy === 'check') {\n let to = path.join(targetPath, templateRender.attributes.to);\n let doesToExist = await fs.pathExists(to);\n\n if (!doesToExist) {\n throw new CarsonTemplateError('STRATEGY_MISSING_FILE', {\n messageParameters: [templateRender.attributes.strategy, templateRender.template.path, to],\n data: {\n path: to,\n },\n });\n }\n }\n }\n\n await fs.ensureDir(path.join(targetPath, CARSON_CONFIG_DIRECTORY));\n\n await saveTemplateRenderSnapshots(templateRenders, templateRenderSnapshotsPath);\n}\n"],"names":["fs","path","prettier","CarsonTemplateError","createUsingTemplateRender","deleteUsingTemplateRender","insertUsingTemplateRender","loadTemplateRenderSnapshots","mergeUsingTemplateRender","overwriteUsingTemplateRender","saveTemplateRenderSnapshots","uninsertUsingTemplateRender","unmergeUsingTemplateRender","CARSON_CONFIG_DIRECTORY","SNAPSHOTS_FILENAME","applyTemplateRenders","templateRenders","targetPath","ignoreStrategies","clearConfigCache","templateRenderSnapshotsPath","join","pathExists","templateRenderSnapshots","templateRenderSnapshot","strategy","attributes","includes","onlyDeleteWhenContentsMatch","templateRender","to","doesToExist","messageParameters","template","data","ensureDir"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/template/applyTemplateRenders.ts"],"sourcesContent":["// TODO: can this be fixed?\n/* eslint-disable complexity -- TODO */\nimport fs from 'fs-extra';\nimport path from 'node:path';\nimport prettier from 'prettier';\n\nimport {CarsonTemplateError} from './CarsonTemplateError.js';\nimport {\n createUsingTemplateRender,\n deleteUsingTemplateRender,\n insertUsingTemplateRender,\n loadTemplateRenderSnapshots,\n mergeUsingTemplateRender,\n overwriteUsingTemplateRender,\n saveTemplateRenderSnapshots,\n uninsertUsingTemplateRender,\n unmergeUsingTemplateRender,\n} from './internals.js';\nimport {type TemplateRenders} from './TemplateRenders.js';\nimport {CARSON_CONFIG_DIRECTORY} from '../constants.js';\n\nexport type ApplyTemplateRendersOptions = {\n templateRenders: TemplateRenders;\n targetPath: string;\n snapshotPath: string;\n ignoreStrategies: string[];\n};\n\n/**\n * Applies rendered Carson template to a specified path, i.e. creates and updates files based on the result of rendering each Carson template file.\n */\nexport async function applyTemplateRenders({\n templateRenders,\n targetPath,\n snapshotPath,\n ignoreStrategies,\n}: ApplyTemplateRendersOptions) {\n // we need to clear Prettier cache, because we are changing file system\n await prettier.clearConfigCache();\n\n // first, we read the existing snapshots and use them to undo previous changes\n if (await fs.pathExists(snapshotPath)) {\n let templateRenderSnapshots = await loadTemplateRenderSnapshots(snapshotPath);\n\n for (let templateRenderSnapshot of templateRenderSnapshots) {\n let strategy =\n typeof templateRenderSnapshot.attributes.strategy === 'string'\n ? templateRenderSnapshot.attributes.strategy\n : templateRenderSnapshot.attributes.strategy[0];\n\n if (ignoreStrategies.includes(strategy)) {\n continue;\n }\n\n if (templateRenderSnapshot.attributes.strategy === 'ensure') {\n await deleteUsingTemplateRender(templateRenderSnapshot, targetPath, {\n onlyDeleteWhenContentsMatch: true,\n });\n }\n\n if (templateRenderSnapshot.attributes.strategy === 'overwrite') {\n await deleteUsingTemplateRender(templateRenderSnapshot, targetPath);\n }\n\n if (templateRenderSnapshot.attributes.strategy === 'merge') {\n await unmergeUsingTemplateRender(templateRenderSnapshot, targetPath);\n }\n\n if (templateRenderSnapshot.attributes.strategy[0] === 'insert') {\n await uninsertUsingTemplateRender(templateRenderSnapshot, targetPath);\n }\n }\n }\n\n // second, we apply strategies that create files\n for (let templateRender of templateRenders) {\n let strategy =\n typeof templateRender.attributes.strategy === 'string'\n ? templateRender.attributes.strategy\n : templateRender.attributes.strategy[0];\n\n if (ignoreStrategies.includes(strategy)) {\n continue;\n }\n\n if (templateRender.attributes.strategy === 'create') {\n await createUsingTemplateRender(templateRender, targetPath);\n }\n\n if (templateRender.attributes.strategy === 'ensure') {\n let to = path.join(targetPath, templateRender.attributes.to);\n let doesToExist = await fs.pathExists(to);\n\n if (!doesToExist) {\n await createUsingTemplateRender(templateRender, targetPath);\n }\n }\n }\n\n // third, we apply strategies that change files\n for (let templateRender of templateRenders) {\n let strategy =\n typeof templateRender.attributes.strategy === 'string'\n ? templateRender.attributes.strategy\n : templateRender.attributes.strategy[0];\n\n if (ignoreStrategies.includes(strategy)) {\n continue;\n }\n\n if (templateRender.attributes.strategy === 'overwrite') {\n await overwriteUsingTemplateRender(templateRender, targetPath);\n }\n\n if (templateRender.attributes.strategy === 'merge') {\n await mergeUsingTemplateRender(templateRender, targetPath);\n }\n\n if (templateRender.attributes.strategy[0] === 'insert') {\n await insertUsingTemplateRender(templateRender, targetPath);\n }\n\n if (templateRender.attributes.strategy === 'check') {\n let to = path.join(targetPath, templateRender.attributes.to);\n let doesToExist = await fs.pathExists(to);\n\n if (!doesToExist) {\n throw new CarsonTemplateError('STRATEGY_MISSING_FILE', {\n messageParameters: [templateRender.attributes.strategy, templateRender.template.path, to],\n data: {\n path: to,\n },\n });\n }\n }\n }\n\n await fs.ensureDir(path.join(targetPath, CARSON_CONFIG_DIRECTORY));\n\n await saveTemplateRenderSnapshots(templateRenders, snapshotPath);\n}\n"],"names":["fs","path","prettier","CarsonTemplateError","createUsingTemplateRender","deleteUsingTemplateRender","insertUsingTemplateRender","loadTemplateRenderSnapshots","mergeUsingTemplateRender","overwriteUsingTemplateRender","saveTemplateRenderSnapshots","uninsertUsingTemplateRender","unmergeUsingTemplateRender","CARSON_CONFIG_DIRECTORY","applyTemplateRenders","templateRenders","targetPath","snapshotPath","ignoreStrategies","clearConfigCache","pathExists","templateRenderSnapshots","templateRenderSnapshot","strategy","attributes","includes","onlyDeleteWhenContentsMatch","templateRender","to","join","doesToExist","messageParameters","template","data","ensureDir"],"mappings":"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"}
@@ -1,12 +1,57 @@
1
1
  import fs from 'fs-extra';
2
+ import json from 'json5';
2
3
  import path from 'node:path';
4
+ import yaml from 'yaml';
5
+ import { merge } from './merge.js';
3
6
  import { prettify } from './prettify.js';
4
7
  export async function createUsingTemplateRender(templateRender, basePath) {
5
8
  let to = path.join(basePath, templateRender.attributes.to);
6
- await fs.ensureDir(path.dirname(to));
7
- await fs.writeFile(to, await prettify(templateRender.content, to), {
8
- encoding: 'utf8'
9
- });
9
+ let extension = path.extname(to);
10
+ switch(extension){
11
+ case '.json':
12
+ {
13
+ if (await fs.pathExists(to)) {
14
+ let targetFile = await fs.readJson(to);
15
+ let fileToMerge = json.parse(templateRender.content);
16
+ let newFile = JSON.stringify(merge(targetFile, fileToMerge), null, 2);
17
+ await fs.writeFile(to, await prettify(newFile, to), {
18
+ encoding: 'utf8'
19
+ });
20
+ } else {
21
+ await fs.ensureDir(path.dirname(to));
22
+ await fs.writeFile(to, await prettify(templateRender.content, to), {
23
+ encoding: 'utf8'
24
+ });
25
+ }
26
+ break;
27
+ }
28
+ case '.yaml':
29
+ {
30
+ if (await fs.pathExists(to)) {
31
+ let targetFile = yaml.parse(await fs.readFile(to, {
32
+ encoding: 'utf8'
33
+ }));
34
+ let fileToMerge = yaml.parse(templateRender.content);
35
+ let newFile = yaml.stringify(merge(targetFile, fileToMerge));
36
+ await fs.writeFile(to, await prettify(newFile, to), {
37
+ encoding: 'utf8'
38
+ });
39
+ } else {
40
+ await fs.writeFile(to, await prettify(templateRender.content, to), {
41
+ encoding: 'utf8'
42
+ });
43
+ }
44
+ break;
45
+ }
46
+ default:
47
+ {
48
+ await fs.ensureDir(path.dirname(to));
49
+ await fs.writeFile(to, await prettify(templateRender.content, to), {
50
+ encoding: 'utf8'
51
+ });
52
+ break;
53
+ }
54
+ }
10
55
  }
11
56
 
12
57
  //# sourceMappingURL=createUsingTemplateRender.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/template/internals/createUsingTemplateRender.ts"],"sourcesContent":["import fs from 'fs-extra';\nimport path from 'node:path';\n\nimport {prettify} from './prettify.js';\nimport {type TemplateRenderSnapshot} from './TemplateRenderSnapshot.js';\nimport {type TemplateRender} from '../TemplateRender.js';\n\nexport async function createUsingTemplateRender(\n templateRender: TemplateRender | TemplateRenderSnapshot,\n basePath: string,\n) {\n let to = path.join(basePath, templateRender.attributes.to);\n\n await fs.ensureDir(path.dirname(to));\n await fs.writeFile(to, await prettify(templateRender.content, to), {encoding: 'utf8'});\n}\n"],"names":["fs","path","prettify","createUsingTemplateRender","templateRender","basePath","to","join","attributes","ensureDir","dirname","writeFile","content","encoding"],"mappings":"AAAA,OAAOA,QAAQ,WAAW;AAC1B,OAAOC,UAAU,YAAY;AAE7B,SAAQC,QAAQ,QAAO,gBAAgB;AAIvC,OAAO,eAAeC,0BACpBC,cAAuD,EACvDC,QAAgB;IAEhB,IAAIC,KAAKL,KAAKM,IAAI,CAACF,UAAUD,eAAeI,UAAU,CAACF,EAAE;IAEzD,MAAMN,GAAGS,SAAS,CAACR,KAAKS,OAAO,CAACJ;IAChC,MAAMN,GAAGW,SAAS,CAACL,IAAI,MAAMJ,SAASE,eAAeQ,OAAO,EAAEN,KAAK;QAACO,UAAU;IAAM;AACtF"}
1
+ {"version":3,"sources":["../../../src/template/internals/createUsingTemplateRender.ts"],"sourcesContent":["import {type Json} from '@jakubmazanec/zod-utils';\nimport fs from 'fs-extra';\nimport json from 'json5';\nimport path from 'node:path';\nimport yaml from 'yaml';\n\nimport {merge} from './merge.js';\nimport {prettify} from './prettify.js';\nimport {type TemplateRenderSnapshot} from './TemplateRenderSnapshot.js';\nimport {type TemplateRender} from '../TemplateRender.js';\n\nexport async function createUsingTemplateRender(\n templateRender: TemplateRender | TemplateRenderSnapshot,\n basePath: string,\n) {\n let to = path.join(basePath, templateRender.attributes.to);\n let extension = path.extname(to);\n\n switch (extension) {\n case '.json': {\n if (await fs.pathExists(to)) {\n let targetFile = (await fs.readJson(to)) as Json;\n let fileToMerge = json.parse<Json>(templateRender.content);\n let newFile = JSON.stringify(merge(targetFile, fileToMerge), null, 2);\n\n await fs.writeFile(to, await prettify(newFile, to), {encoding: 'utf8'});\n } else {\n await fs.ensureDir(path.dirname(to));\n await fs.writeFile(to, await prettify(templateRender.content, to), {encoding: 'utf8'});\n }\n\n break;\n }\n\n case '.yaml': {\n if (await fs.pathExists(to)) {\n let targetFile = yaml.parse(await fs.readFile(to, {encoding: 'utf8'})) as unknown;\n let fileToMerge = yaml.parse(templateRender.content) as unknown;\n let newFile = yaml.stringify(merge(targetFile, fileToMerge));\n\n await fs.writeFile(to, await prettify(newFile, to), {encoding: 'utf8'});\n } else {\n await fs.writeFile(to, await prettify(templateRender.content, to), {encoding: 'utf8'});\n }\n\n break;\n }\n\n default: {\n await fs.ensureDir(path.dirname(to));\n await fs.writeFile(to, await prettify(templateRender.content, to), {encoding: 'utf8'});\n\n break;\n }\n }\n}\n"],"names":["fs","json","path","yaml","merge","prettify","createUsingTemplateRender","templateRender","basePath","to","join","attributes","extension","extname","pathExists","targetFile","readJson","fileToMerge","parse","content","newFile","JSON","stringify","writeFile","encoding","ensureDir","dirname","readFile"],"mappings":"AACA,OAAOA,QAAQ,WAAW;AAC1B,OAAOC,UAAU,QAAQ;AACzB,OAAOC,UAAU,YAAY;AAC7B,OAAOC,UAAU,OAAO;AAExB,SAAQC,KAAK,QAAO,aAAa;AACjC,SAAQC,QAAQ,QAAO,gBAAgB;AAIvC,OAAO,eAAeC,0BACpBC,cAAuD,EACvDC,QAAgB;IAEhB,IAAIC,KAAKP,KAAKQ,IAAI,CAACF,UAAUD,eAAeI,UAAU,CAACF,EAAE;IACzD,IAAIG,YAAYV,KAAKW,OAAO,CAACJ;IAE7B,OAAQG;QACN,KAAK;YAAS;gBACZ,IAAI,MAAMZ,GAAGc,UAAU,CAACL,KAAK;oBAC3B,IAAIM,aAAc,MAAMf,GAAGgB,QAAQ,CAACP;oBACpC,IAAIQ,cAAchB,KAAKiB,KAAK,CAAOX,eAAeY,OAAO;oBACzD,IAAIC,UAAUC,KAAKC,SAAS,CAAClB,MAAMW,YAAYE,cAAc,MAAM;oBAEnE,MAAMjB,GAAGuB,SAAS,CAACd,IAAI,MAAMJ,SAASe,SAASX,KAAK;wBAACe,UAAU;oBAAM;gBACvE,OAAO;oBACL,MAAMxB,GAAGyB,SAAS,CAACvB,KAAKwB,OAAO,CAACjB;oBAChC,MAAMT,GAAGuB,SAAS,CAACd,IAAI,MAAMJ,SAASE,eAAeY,OAAO,EAAEV,KAAK;wBAACe,UAAU;oBAAM;gBACtF;gBAEA;YACF;QAEA,KAAK;YAAS;gBACZ,IAAI,MAAMxB,GAAGc,UAAU,CAACL,KAAK;oBAC3B,IAAIM,aAAaZ,KAAKe,KAAK,CAAC,MAAMlB,GAAG2B,QAAQ,CAAClB,IAAI;wBAACe,UAAU;oBAAM;oBACnE,IAAIP,cAAcd,KAAKe,KAAK,CAACX,eAAeY,OAAO;oBACnD,IAAIC,UAAUjB,KAAKmB,SAAS,CAAClB,MAAMW,YAAYE;oBAE/C,MAAMjB,GAAGuB,SAAS,CAACd,IAAI,MAAMJ,SAASe,SAASX,KAAK;wBAACe,UAAU;oBAAM;gBACvE,OAAO;oBACL,MAAMxB,GAAGuB,SAAS,CAACd,IAAI,MAAMJ,SAASE,eAAeY,OAAO,EAAEV,KAAK;wBAACe,UAAU;oBAAM;gBACtF;gBAEA;YACF;QAEA;YAAS;gBACP,MAAMxB,GAAGyB,SAAS,CAACvB,KAAKwB,OAAO,CAACjB;gBAChC,MAAMT,GAAGuB,SAAS,CAACd,IAAI,MAAMJ,SAASE,eAAeY,OAAO,EAAEV,KAAK;oBAACe,UAAU;gBAAM;gBAEpF;YACF;IACF;AACF"}
@@ -11,24 +11,36 @@ export async function mergeUsingTemplateRender(templateRender, basePath) {
11
11
  switch(extension){
12
12
  case '.json':
13
13
  {
14
- let targetFile = await fs.readJson(to);
15
- let fileToMerge = json.parse(templateRender.content);
16
- let newFile = JSON.stringify(merge(targetFile, fileToMerge));
17
- await fs.writeFile(to, await prettify(newFile, to), {
18
- encoding: 'utf8'
19
- });
14
+ if (await fs.pathExists(to)) {
15
+ let targetFile = await fs.readJson(to);
16
+ let fileToMerge = json.parse(templateRender.content);
17
+ let newFile = JSON.stringify(merge(targetFile, fileToMerge), null, 2);
18
+ await fs.writeFile(to, await prettify(newFile, to), {
19
+ encoding: 'utf8'
20
+ });
21
+ } else {
22
+ await fs.writeFile(to, await prettify(templateRender.content, to), {
23
+ encoding: 'utf8'
24
+ });
25
+ }
20
26
  break;
21
27
  }
22
28
  case '.yaml':
23
29
  {
24
- let targetFile = yaml.parse(await fs.readFile(to, {
25
- encoding: 'utf8'
26
- }));
27
- let fileToMerge = yaml.parse(templateRender.content);
28
- let newFile = yaml.stringify(merge(targetFile, fileToMerge));
29
- await fs.writeFile(to, await prettify(newFile, to), {
30
- encoding: 'utf8'
31
- });
30
+ if (await fs.pathExists(to)) {
31
+ let targetFile = yaml.parse(await fs.readFile(to, {
32
+ encoding: 'utf8'
33
+ }));
34
+ let fileToMerge = yaml.parse(templateRender.content);
35
+ let newFile = yaml.stringify(merge(targetFile, fileToMerge));
36
+ await fs.writeFile(to, await prettify(newFile, to), {
37
+ encoding: 'utf8'
38
+ });
39
+ } else {
40
+ await fs.writeFile(to, await prettify(templateRender.content, to), {
41
+ encoding: 'utf8'
42
+ });
43
+ }
32
44
  break;
33
45
  }
34
46
  default:
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/template/internals/mergeUsingTemplateRender.ts"],"sourcesContent":["import {type Json} from '@jakubmazanec/zod-utils';\nimport fs from 'fs-extra';\nimport json from 'json5';\nimport path from 'node:path';\nimport yaml from 'yaml';\n\nimport {merge} from './merge.js';\nimport {prettify} from './prettify.js';\nimport {type TemplateRenderSnapshot} from './TemplateRenderSnapshot.js';\nimport {CarsonTemplateError} from '../CarsonTemplateError.js';\nimport {type TemplateRender} from '../TemplateRender.js';\n\nexport async function mergeUsingTemplateRender(\n templateRender: TemplateRender | TemplateRenderSnapshot,\n basePath: string,\n) {\n let to = path.join(basePath, templateRender.attributes.to);\n let extension = path.extname(to);\n\n switch (extension) {\n case '.json': {\n let targetFile = (await fs.readJson(to)) as Json;\n let fileToMerge = json.parse<Json>(templateRender.content);\n let newFile = JSON.stringify(merge(targetFile, fileToMerge));\n\n await fs.writeFile(to, await prettify(newFile, to), {encoding: 'utf8'});\n\n break;\n }\n\n case '.yaml': {\n let targetFile = yaml.parse(await fs.readFile(to, {encoding: 'utf8'})) as unknown;\n let fileToMerge = yaml.parse(templateRender.content) as unknown;\n let newFile = yaml.stringify(merge(targetFile, fileToMerge));\n\n await fs.writeFile(to, await prettify(newFile, to), {encoding: 'utf8'});\n\n break;\n }\n\n default: {\n throw new CarsonTemplateError('STRATEGY_UNSUPPORTED_EXTENSION', {\n messageParameters: [\n templateRender.attributes.strategy,\n templateRender.template.path,\n extension,\n ],\n data: {\n path: templateRender.template.path,\n },\n });\n }\n }\n}\n"],"names":["fs","json","path","yaml","merge","prettify","CarsonTemplateError","mergeUsingTemplateRender","templateRender","basePath","to","join","attributes","extension","extname","targetFile","readJson","fileToMerge","parse","content","newFile","JSON","stringify","writeFile","encoding","readFile","messageParameters","strategy","template","data"],"mappings":"AACA,OAAOA,QAAQ,WAAW;AAC1B,OAAOC,UAAU,QAAQ;AACzB,OAAOC,UAAU,YAAY;AAC7B,OAAOC,UAAU,OAAO;AAExB,SAAQC,KAAK,QAAO,aAAa;AACjC,SAAQC,QAAQ,QAAO,gBAAgB;AAEvC,SAAQC,mBAAmB,QAAO,4BAA4B;AAG9D,OAAO,eAAeC,yBACpBC,cAAuD,EACvDC,QAAgB;IAEhB,IAAIC,KAAKR,KAAKS,IAAI,CAACF,UAAUD,eAAeI,UAAU,CAACF,EAAE;IACzD,IAAIG,YAAYX,KAAKY,OAAO,CAACJ;IAE7B,OAAQG;QACN,KAAK;YAAS;gBACZ,IAAIE,aAAc,MAAMf,GAAGgB,QAAQ,CAACN;gBACpC,IAAIO,cAAchB,KAAKiB,KAAK,CAAOV,eAAeW,OAAO;gBACzD,IAAIC,UAAUC,KAAKC,SAAS,CAAClB,MAAMW,YAAYE;gBAE/C,MAAMjB,GAAGuB,SAAS,CAACb,IAAI,MAAML,SAASe,SAASV,KAAK;oBAACc,UAAU;gBAAM;gBAErE;YACF;QAEA,KAAK;YAAS;gBACZ,IAAIT,aAAaZ,KAAKe,KAAK,CAAC,MAAMlB,GAAGyB,QAAQ,CAACf,IAAI;oBAACc,UAAU;gBAAM;gBACnE,IAAIP,cAAcd,KAAKe,KAAK,CAACV,eAAeW,OAAO;gBACnD,IAAIC,UAAUjB,KAAKmB,SAAS,CAAClB,MAAMW,YAAYE;gBAE/C,MAAMjB,GAAGuB,SAAS,CAACb,IAAI,MAAML,SAASe,SAASV,KAAK;oBAACc,UAAU;gBAAM;gBAErE;YACF;QAEA;YAAS;gBACP,MAAM,IAAIlB,oBAAoB,kCAAkC;oBAC9DoB,mBAAmB;wBACjBlB,eAAeI,UAAU,CAACe,QAAQ;wBAClCnB,eAAeoB,QAAQ,CAAC1B,IAAI;wBAC5BW;qBACD;oBACDgB,MAAM;wBACJ3B,MAAMM,eAAeoB,QAAQ,CAAC1B,IAAI;oBACpC;gBACF;YACF;IACF;AACF"}
1
+ {"version":3,"sources":["../../../src/template/internals/mergeUsingTemplateRender.ts"],"sourcesContent":["import {type Json} from '@jakubmazanec/zod-utils';\nimport fs from 'fs-extra';\nimport json from 'json5';\nimport path from 'node:path';\nimport yaml from 'yaml';\n\nimport {merge} from './merge.js';\nimport {prettify} from './prettify.js';\nimport {type TemplateRenderSnapshot} from './TemplateRenderSnapshot.js';\nimport {CarsonTemplateError} from '../CarsonTemplateError.js';\nimport {type TemplateRender} from '../TemplateRender.js';\n\nexport async function mergeUsingTemplateRender(\n templateRender: TemplateRender | TemplateRenderSnapshot,\n basePath: string,\n) {\n let to = path.join(basePath, templateRender.attributes.to);\n let extension = path.extname(to);\n\n switch (extension) {\n case '.json': {\n if (await fs.pathExists(to)) {\n let targetFile = (await fs.readJson(to)) as Json;\n let fileToMerge = json.parse<Json>(templateRender.content);\n let newFile = JSON.stringify(merge(targetFile, fileToMerge), null, 2);\n\n await fs.writeFile(to, await prettify(newFile, to), {encoding: 'utf8'});\n } else {\n await fs.writeFile(to, await prettify(templateRender.content, to), {encoding: 'utf8'});\n }\n\n break;\n }\n\n case '.yaml': {\n if (await fs.pathExists(to)) {\n let targetFile = yaml.parse(await fs.readFile(to, {encoding: 'utf8'})) as unknown;\n let fileToMerge = yaml.parse(templateRender.content) as unknown;\n let newFile = yaml.stringify(merge(targetFile, fileToMerge));\n\n await fs.writeFile(to, await prettify(newFile, to), {encoding: 'utf8'});\n } else {\n await fs.writeFile(to, await prettify(templateRender.content, to), {encoding: 'utf8'});\n }\n\n break;\n }\n\n default: {\n throw new CarsonTemplateError('STRATEGY_UNSUPPORTED_EXTENSION', {\n messageParameters: [\n templateRender.attributes.strategy,\n templateRender.template.path,\n extension,\n ],\n data: {\n path: templateRender.template.path,\n },\n });\n }\n }\n}\n"],"names":["fs","json","path","yaml","merge","prettify","CarsonTemplateError","mergeUsingTemplateRender","templateRender","basePath","to","join","attributes","extension","extname","pathExists","targetFile","readJson","fileToMerge","parse","content","newFile","JSON","stringify","writeFile","encoding","readFile","messageParameters","strategy","template","data"],"mappings":"AACA,OAAOA,QAAQ,WAAW;AAC1B,OAAOC,UAAU,QAAQ;AACzB,OAAOC,UAAU,YAAY;AAC7B,OAAOC,UAAU,OAAO;AAExB,SAAQC,KAAK,QAAO,aAAa;AACjC,SAAQC,QAAQ,QAAO,gBAAgB;AAEvC,SAAQC,mBAAmB,QAAO,4BAA4B;AAG9D,OAAO,eAAeC,yBACpBC,cAAuD,EACvDC,QAAgB;IAEhB,IAAIC,KAAKR,KAAKS,IAAI,CAACF,UAAUD,eAAeI,UAAU,CAACF,EAAE;IACzD,IAAIG,YAAYX,KAAKY,OAAO,CAACJ;IAE7B,OAAQG;QACN,KAAK;YAAS;gBACZ,IAAI,MAAMb,GAAGe,UAAU,CAACL,KAAK;oBAC3B,IAAIM,aAAc,MAAMhB,GAAGiB,QAAQ,CAACP;oBACpC,IAAIQ,cAAcjB,KAAKkB,KAAK,CAAOX,eAAeY,OAAO;oBACzD,IAAIC,UAAUC,KAAKC,SAAS,CAACnB,MAAMY,YAAYE,cAAc,MAAM;oBAEnE,MAAMlB,GAAGwB,SAAS,CAACd,IAAI,MAAML,SAASgB,SAASX,KAAK;wBAACe,UAAU;oBAAM;gBACvE,OAAO;oBACL,MAAMzB,GAAGwB,SAAS,CAACd,IAAI,MAAML,SAASG,eAAeY,OAAO,EAAEV,KAAK;wBAACe,UAAU;oBAAM;gBACtF;gBAEA;YACF;QAEA,KAAK;YAAS;gBACZ,IAAI,MAAMzB,GAAGe,UAAU,CAACL,KAAK;oBAC3B,IAAIM,aAAab,KAAKgB,KAAK,CAAC,MAAMnB,GAAG0B,QAAQ,CAAChB,IAAI;wBAACe,UAAU;oBAAM;oBACnE,IAAIP,cAAcf,KAAKgB,KAAK,CAACX,eAAeY,OAAO;oBACnD,IAAIC,UAAUlB,KAAKoB,SAAS,CAACnB,MAAMY,YAAYE;oBAE/C,MAAMlB,GAAGwB,SAAS,CAACd,IAAI,MAAML,SAASgB,SAASX,KAAK;wBAACe,UAAU;oBAAM;gBACvE,OAAO;oBACL,MAAMzB,GAAGwB,SAAS,CAACd,IAAI,MAAML,SAASG,eAAeY,OAAO,EAAEV,KAAK;wBAACe,UAAU;oBAAM;gBACtF;gBAEA;YACF;QAEA;YAAS;gBACP,MAAM,IAAInB,oBAAoB,kCAAkC;oBAC9DqB,mBAAmB;wBACjBnB,eAAeI,UAAU,CAACgB,QAAQ;wBAClCpB,eAAeqB,QAAQ,CAAC3B,IAAI;wBAC5BW;qBACD;oBACDiB,MAAM;wBACJ5B,MAAMM,eAAeqB,QAAQ,CAAC3B,IAAI;oBACpC;gBACF;YACF;IACF;AACF"}
@@ -7,7 +7,7 @@ import { z } from 'zod';
7
7
  import { projectConfigSchema } from './ProjectConfig.js';
8
8
  import { ProjectError } from './ProjectError.js';
9
9
  import { saveProjectConfig } from './saveProjectConfig.js';
10
- import { CARSON_CONFIG_DIRECTORY, PROJECT_CONFIG_FILENAME } from '../constants.js';
10
+ import { CARSON_CONFIG_DIRECTORY, PROJECT_CONFIG_FILENAME, PROJECT_SNAPSHOT_FILENAME } from '../constants.js';
11
11
  import { applyTemplateRenders } from '../template/applyTemplateRenders.js';
12
12
  import { renderCarsonTemplate } from '../template/renderCarsonTemplate.js';
13
13
  /**
@@ -56,17 +56,20 @@ import { renderCarsonTemplate } from '../template/renderCarsonTemplate.js';
56
56
  },
57
57
  workspace
58
58
  });
59
- try {
60
- await ensureEmptyDirectory(project.path);
61
- } catch {
62
- throw new ProjectError('PROJECT_PATH_NOT_EMPTY', {
63
- messageParameters: [
64
- project.path
65
- ],
66
- data: {
67
- path: project.path
68
- }
69
- });
59
+ // if the workspace is single-project, it shares a directory with the project, so we can't insist on it being an empty directory
60
+ if (workspace.isMultiProject) {
61
+ try {
62
+ await ensureEmptyDirectory(project.path);
63
+ } catch {
64
+ throw new ProjectError('PROJECT_PATH_NOT_EMPTY', {
65
+ messageParameters: [
66
+ project.path
67
+ ],
68
+ data: {
69
+ path: project.path
70
+ }
71
+ });
72
+ }
70
73
  }
71
74
  let templateRenders = await renderCarsonTemplate({
72
75
  templateId,
@@ -78,7 +81,8 @@ import { renderCarsonTemplate } from '../template/renderCarsonTemplate.js';
78
81
  });
79
82
  await applyTemplateRenders({
80
83
  templateRenders,
81
- path: project.path,
84
+ targetPath: project.path,
85
+ snapshotPath: path.join(project.path, CARSON_CONFIG_DIRECTORY, PROJECT_SNAPSHOT_FILENAME),
82
86
  ignoreStrategies: [
83
87
  'check'
84
88
  ]
@@ -186,7 +190,8 @@ import { renderCarsonTemplate } from '../template/renderCarsonTemplate.js';
186
190
  });
187
191
  await applyTemplateRenders({
188
192
  templateRenders,
189
- path: this.path,
193
+ targetPath: this.path,
194
+ snapshotPath: path.join(this.path, CARSON_CONFIG_DIRECTORY, PROJECT_SNAPSHOT_FILENAME),
190
195
  ignoreStrategies: [
191
196
  'create'
192
197
  ]
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/workspace/Project.ts"],"sourcesContent":["import {ensureEmptyDirectory, pathToPosixPath} from '@jakubmazanec/fs-utils';\nimport {readFile, packageJsonSchema, type PackageJson} from '@jakubmazanec/zod-utils';\nimport fs from 'fs-extra';\nimport json5 from 'json5';\nimport path from 'node:path';\nimport {z} from 'zod';\n\nimport {type ProjectConfig, projectConfigSchema} from './ProjectConfig.js';\nimport {type ProjectCreateOptions} from './ProjectCreateOptions.js';\nimport {ProjectError} from './ProjectError.js';\nimport {type ProjectOptions} from './ProjectOptions.js';\nimport {type ProjectUpdateOptions} from './ProjectUpdateOptions.js';\nimport {saveProjectConfig} from './saveProjectConfig.js';\nimport {type Workspace} from './Workspace.js';\nimport {CARSON_CONFIG_DIRECTORY, PROJECT_CONFIG_FILENAME} from '../constants.js';\nimport {applyTemplateRenders} from '../template/applyTemplateRenders.js';\nimport {renderCarsonTemplate} from '../template/renderCarsonTemplate.js';\n\n/**\n * Project represent a self-contained piece of software (e.g. a package, or an app) that is part of a {@link Workspace}. Currently only Node.js projects are supported.\n *\n * @typeParam M If true, the project belongs to a multi-project workspace.\n */\nexport class Project<M extends boolean = true> {\n /** Project name. */\n name?: string;\n\n /** Project path. */\n path: string;\n\n /** Project path relative to workspace path. */\n relativePath: string;\n\n /** Project configuration; if it's not found, the default is an empty object. */\n config: ProjectConfig;\n\n /** Parsed project `package.json` file; if it's not found, the default is an empty object. */\n packageJson: PackageJson;\n\n /** Parent {@link Workspace} instance. */\n workspace: Workspace<M>;\n\n /** Errors found during reading from the file system. */\n errors: Array<InstanceType<typeof ProjectError>> = [];\n\n constructor({\n path: projectPath,\n name,\n config,\n packageJson,\n workspace,\n errors,\n }: ProjectOptions<M>) {\n // TODO: check that this path and workspace path are correct, i.e. project path must always be inside of workspace path\n this.path = projectPath;\n\n if (name) {\n this.name = name;\n }\n\n if (config) {\n this.config = config;\n } else {\n this.config = {};\n }\n\n if (packageJson) {\n this.packageJson = packageJson;\n } else {\n this.packageJson = {};\n }\n\n this.workspace = workspace;\n\n if (errors) {\n this.errors = errors;\n }\n\n if (this.packageJson.name?.length) {\n this.name = this.packageJson.name;\n }\n\n this.relativePath = pathToPosixPath(path.relative(this.workspace.path, this.path));\n }\n\n static async create<M extends boolean>({\n templateId,\n workspace,\n projectPath,\n projectName,\n args,\n }: ProjectCreateOptions<M>): Promise<Project<M>> {\n let project = new this({\n path: projectPath,\n name: projectName,\n config: {\n template: templateId,\n },\n workspace,\n });\n\n try {\n await ensureEmptyDirectory(project.path);\n } catch {\n throw new ProjectError('PROJECT_PATH_NOT_EMPTY', {\n messageParameters: [project.path],\n data: {\n path: project.path,\n },\n });\n }\n\n let templateRenders = await renderCarsonTemplate({\n templateId,\n templateData: {\n workspace,\n project,\n args,\n },\n });\n\n await applyTemplateRenders({\n templateRenders,\n path: project.path,\n ignoreStrategies: ['check'],\n });\n\n await saveProjectConfig({projectPath: project.path, projectConfig: {template: templateId}});\n\n await project.read();\n\n return project;\n }\n\n static async read<M extends boolean>(\n projectPath: string,\n workspace: Workspace<M>,\n ): Promise<Project<M>> {\n let project = new this({\n path: projectPath,\n workspace,\n });\n\n await project.read();\n\n return project;\n }\n\n async read() {\n if (!(await fs.pathExists(this.path))) {\n throw new ProjectError('NO_SUCH_PROJECT_PATH', {\n messageParameters: [this.path],\n data: {\n path: this.path,\n },\n });\n }\n\n let errors: Array<InstanceType<typeof ProjectError>> = [];\n\n // load project config\n let projectConfigPath = path.join(this.path, CARSON_CONFIG_DIRECTORY, PROJECT_CONFIG_FILENAME);\n let doesProjectConfigExist = await fs.pathExists(projectConfigPath);\n let projectConfig: ProjectConfig | z.ZodError;\n\n if (doesProjectConfigExist) {\n projectConfig = await readFile(projectConfigPath, projectConfigSchema, {\n parser: (rawString: string) => json5.parse<unknown>(rawString),\n });\n } else {\n projectConfig = {};\n }\n\n if (projectConfig instanceof z.ZodError) {\n let zodError = projectConfig;\n\n errors.push(\n new ProjectError('INVALID_PROJECT_CONFIG', {\n messageParameters: [projectConfigPath],\n data: {\n path: projectConfigPath,\n zodIssues: zodError.issues,\n },\n }),\n );\n\n projectConfig = {};\n }\n\n // load package.json\n let packageJsonPath = path.join(this.path, 'package.json');\n let doesPackageJsonExist = await fs.pathExists(packageJsonPath);\n let packageJson: PackageJson | z.ZodError;\n\n if (doesPackageJsonExist) {\n packageJson = await readFile(packageJsonPath, packageJsonSchema, {\n parser: (rawString: string) => json5.parse<unknown>(rawString),\n });\n } else {\n packageJson = {};\n }\n\n if (packageJson instanceof z.ZodError) {\n let zodError = packageJson;\n\n errors.push(\n new ProjectError('INVALID_PACKAGE_JSON', {\n messageParameters: [packageJsonPath],\n data: {\n path: packageJsonPath,\n zodIssues: zodError.issues,\n },\n }),\n );\n\n packageJson = {};\n }\n\n if (packageJson.name?.length) {\n this.name = packageJson.name;\n }\n\n this.config = projectConfig;\n this.packageJson = packageJson;\n this.errors = errors;\n\n return this;\n }\n\n async update({args}: ProjectUpdateOptions): Promise<this> {\n if (!this.config.template) {\n throw new ProjectError('MISSING_CARSON_TEMPLATE_ID', {\n messageParameters: [path.join(this.path, CARSON_CONFIG_DIRECTORY, PROJECT_CONFIG_FILENAME)],\n });\n }\n\n let templateRenders = await renderCarsonTemplate({\n templateId: this.config.template,\n templateData: {\n workspace: this.workspace,\n project: this,\n args,\n },\n });\n\n await applyTemplateRenders({\n templateRenders,\n path: this.path,\n ignoreStrategies: ['create'],\n });\n\n await this.read();\n\n return this;\n }\n}\n"],"names":["ensureEmptyDirectory","pathToPosixPath","readFile","packageJsonSchema","fs","json5","path","z","projectConfigSchema","ProjectError","saveProjectConfig","CARSON_CONFIG_DIRECTORY","PROJECT_CONFIG_FILENAME","applyTemplateRenders","renderCarsonTemplate","Project","name","relativePath","config","packageJson","workspace","errors","constructor","projectPath","length","relative","create","templateId","projectName","args","project","template","messageParameters","data","templateRenders","templateData","ignoreStrategies","projectConfig","read","pathExists","projectConfigPath","join","doesProjectConfigExist","parser","rawString","parse","ZodError","zodError","push","zodIssues","issues","packageJsonPath","doesPackageJsonExist","update"],"mappings":"AAAA,SAAQA,oBAAoB,EAAEC,eAAe,QAAO,yBAAyB;AAC7E,SAAQC,QAAQ,EAAEC,iBAAiB,QAAyB,0BAA0B;AACtF,OAAOC,QAAQ,WAAW;AAC1B,OAAOC,WAAW,QAAQ;AAC1B,OAAOC,UAAU,YAAY;AAC7B,SAAQC,CAAC,QAAO,MAAM;AAEtB,SAA4BC,mBAAmB,QAAO,qBAAqB;AAE3E,SAAQC,YAAY,QAAO,oBAAoB;AAG/C,SAAQC,iBAAiB,QAAO,yBAAyB;AAEzD,SAAQC,uBAAuB,EAAEC,uBAAuB,QAAO,kBAAkB;AACjF,SAAQC,oBAAoB,QAAO,sCAAsC;AACzE,SAAQC,oBAAoB,QAAO,sCAAsC;AAEzE;;;;CAIC,GACD,OAAO,MAAMC;IACX,kBAAkB,GAClBC,KAAc;IAEd,kBAAkB,GAClBV,KAAa;IAEb,6CAA6C,GAC7CW,aAAqB;IAErB,8EAA8E,GAC9EC,OAAsB;IAEtB,2FAA2F,GAC3FC,YAAyB;IAEzB,uCAAuC,GACvCC,UAAwB;IAExB,sDAAsD,GACtDC,SAAmD,EAAE,CAAC;IAEtDC,YAAY,EACVhB,MAAMiB,WAAW,EACjBP,IAAI,EACJE,MAAM,EACNC,WAAW,EACXC,SAAS,EACTC,MAAM,EACY,CAAE;QACpB,uHAAuH;QACvH,IAAI,CAACf,IAAI,GAAGiB;QAEZ,IAAIP,MAAM;YACR,IAAI,CAACA,IAAI,GAAGA;QACd;QAEA,IAAIE,QAAQ;YACV,IAAI,CAACA,MAAM,GAAGA;QAChB,OAAO;YACL,IAAI,CAACA,MAAM,GAAG,CAAC;QACjB;QAEA,IAAIC,aAAa;YACf,IAAI,CAACA,WAAW,GAAGA;QACrB,OAAO;YACL,IAAI,CAACA,WAAW,GAAG,CAAC;QACtB;QAEA,IAAI,CAACC,SAAS,GAAGA;QAEjB,IAAIC,QAAQ;YACV,IAAI,CAACA,MAAM,GAAGA;QAChB;QAEA,IAAI,IAAI,CAACF,WAAW,CAACH,IAAI,EAAEQ,QAAQ;YACjC,IAAI,CAACR,IAAI,GAAG,IAAI,CAACG,WAAW,CAACH,IAAI;QACnC;QAEA,IAAI,CAACC,YAAY,GAAGhB,gBAAgBK,KAAKmB,QAAQ,CAAC,IAAI,CAACL,SAAS,CAACd,IAAI,EAAE,IAAI,CAACA,IAAI;IAClF;IAEA,aAAaoB,OAA0B,EACrCC,UAAU,EACVP,SAAS,EACTG,WAAW,EACXK,WAAW,EACXC,IAAI,EACoB,EAAuB;QAC/C,IAAIC,UAAU,IAAI,IAAI,CAAC;YACrBxB,MAAMiB;YACNP,MAAMY;YACNV,QAAQ;gBACNa,UAAUJ;YACZ;YACAP;QACF;QAEA,IAAI;YACF,MAAMpB,qBAAqB8B,QAAQxB,IAAI;QACzC,EAAE,OAAM;YACN,MAAM,IAAIG,aAAa,0BAA0B;gBAC/CuB,mBAAmB;oBAACF,QAAQxB,IAAI;iBAAC;gBACjC2B,MAAM;oBACJ3B,MAAMwB,QAAQxB,IAAI;gBACpB;YACF;QACF;QAEA,IAAI4B,kBAAkB,MAAMpB,qBAAqB;YAC/Ca;YACAQ,cAAc;gBACZf;gBACAU;gBACAD;YACF;QACF;QAEA,MAAMhB,qBAAqB;YACzBqB;YACA5B,MAAMwB,QAAQxB,IAAI;YAClB8B,kBAAkB;gBAAC;aAAQ;QAC7B;QAEA,MAAM1B,kBAAkB;YAACa,aAAaO,QAAQxB,IAAI;YAAE+B,eAAe;gBAACN,UAAUJ;YAAU;QAAC;QAEzF,MAAMG,QAAQQ,IAAI;QAElB,OAAOR;IACT;IAEA,aAAaQ,KACXf,WAAmB,EACnBH,SAAuB,EACF;QACrB,IAAIU,UAAU,IAAI,IAAI,CAAC;YACrBxB,MAAMiB;YACNH;QACF;QAEA,MAAMU,QAAQQ,IAAI;QAElB,OAAOR;IACT;IAEA,MAAMQ,OAAO;QACX,IAAI,CAAE,MAAMlC,GAAGmC,UAAU,CAAC,IAAI,CAACjC,IAAI,GAAI;YACrC,MAAM,IAAIG,aAAa,wBAAwB;gBAC7CuB,mBAAmB;oBAAC,IAAI,CAAC1B,IAAI;iBAAC;gBAC9B2B,MAAM;oBACJ3B,MAAM,IAAI,CAACA,IAAI;gBACjB;YACF;QACF;QAEA,IAAIe,SAAmD,EAAE;QAEzD,sBAAsB;QACtB,IAAImB,oBAAoBlC,KAAKmC,IAAI,CAAC,IAAI,CAACnC,IAAI,EAAEK,yBAAyBC;QACtE,IAAI8B,yBAAyB,MAAMtC,GAAGmC,UAAU,CAACC;QACjD,IAAIH;QAEJ,IAAIK,wBAAwB;YAC1BL,gBAAgB,MAAMnC,SAASsC,mBAAmBhC,qBAAqB;gBACrEmC,QAAQ,CAACC,YAAsBvC,MAAMwC,KAAK,CAAUD;YACtD;QACF,OAAO;YACLP,gBAAgB,CAAC;QACnB;QAEA,IAAIA,yBAAyB9B,EAAEuC,QAAQ,EAAE;YACvC,IAAIC,WAAWV;YAEfhB,OAAO2B,IAAI,CACT,IAAIvC,aAAa,0BAA0B;gBACzCuB,mBAAmB;oBAACQ;iBAAkB;gBACtCP,MAAM;oBACJ3B,MAAMkC;oBACNS,WAAWF,SAASG,MAAM;gBAC5B;YACF;YAGFb,gBAAgB,CAAC;QACnB;QAEA,oBAAoB;QACpB,IAAIc,kBAAkB7C,KAAKmC,IAAI,CAAC,IAAI,CAACnC,IAAI,EAAE;QAC3C,IAAI8C,uBAAuB,MAAMhD,GAAGmC,UAAU,CAACY;QAC/C,IAAIhC;QAEJ,IAAIiC,sBAAsB;YACxBjC,cAAc,MAAMjB,SAASiD,iBAAiBhD,mBAAmB;gBAC/DwC,QAAQ,CAACC,YAAsBvC,MAAMwC,KAAK,CAAUD;YACtD;QACF,OAAO;YACLzB,cAAc,CAAC;QACjB;QAEA,IAAIA,uBAAuBZ,EAAEuC,QAAQ,EAAE;YACrC,IAAIC,WAAW5B;YAEfE,OAAO2B,IAAI,CACT,IAAIvC,aAAa,wBAAwB;gBACvCuB,mBAAmB;oBAACmB;iBAAgB;gBACpClB,MAAM;oBACJ3B,MAAM6C;oBACNF,WAAWF,SAASG,MAAM;gBAC5B;YACF;YAGF/B,cAAc,CAAC;QACjB;QAEA,IAAIA,YAAYH,IAAI,EAAEQ,QAAQ;YAC5B,IAAI,CAACR,IAAI,GAAGG,YAAYH,IAAI;QAC9B;QAEA,IAAI,CAACE,MAAM,GAAGmB;QACd,IAAI,CAAClB,WAAW,GAAGA;QACnB,IAAI,CAACE,MAAM,GAAGA;QAEd,OAAO,IAAI;IACb;IAEA,MAAMgC,OAAO,EAACxB,IAAI,EAAuB,EAAiB;QACxD,IAAI,CAAC,IAAI,CAACX,MAAM,CAACa,QAAQ,EAAE;YACzB,MAAM,IAAItB,aAAa,8BAA8B;gBACnDuB,mBAAmB;oBAAC1B,KAAKmC,IAAI,CAAC,IAAI,CAACnC,IAAI,EAAEK,yBAAyBC;iBAAyB;YAC7F;QACF;QAEA,IAAIsB,kBAAkB,MAAMpB,qBAAqB;YAC/Ca,YAAY,IAAI,CAACT,MAAM,CAACa,QAAQ;YAChCI,cAAc;gBACZf,WAAW,IAAI,CAACA,SAAS;gBACzBU,SAAS,IAAI;gBACbD;YACF;QACF;QAEA,MAAMhB,qBAAqB;YACzBqB;YACA5B,MAAM,IAAI,CAACA,IAAI;YACf8B,kBAAkB;gBAAC;aAAS;QAC9B;QAEA,MAAM,IAAI,CAACE,IAAI;QAEf,OAAO,IAAI;IACb;AACF"}
1
+ {"version":3,"sources":["../../src/workspace/Project.ts"],"sourcesContent":["import {ensureEmptyDirectory, pathToPosixPath} from '@jakubmazanec/fs-utils';\nimport {readFile, packageJsonSchema, type PackageJson} from '@jakubmazanec/zod-utils';\nimport fs from 'fs-extra';\nimport json5 from 'json5';\nimport path from 'node:path';\nimport {z} from 'zod';\n\nimport {type ProjectConfig, projectConfigSchema} from './ProjectConfig.js';\nimport {type ProjectCreateOptions} from './ProjectCreateOptions.js';\nimport {ProjectError} from './ProjectError.js';\nimport {type ProjectOptions} from './ProjectOptions.js';\nimport {type ProjectUpdateOptions} from './ProjectUpdateOptions.js';\nimport {saveProjectConfig} from './saveProjectConfig.js';\nimport {type Workspace} from './Workspace.js';\nimport {\n CARSON_CONFIG_DIRECTORY,\n PROJECT_CONFIG_FILENAME,\n PROJECT_SNAPSHOT_FILENAME,\n} from '../constants.js';\nimport {applyTemplateRenders} from '../template/applyTemplateRenders.js';\nimport {renderCarsonTemplate} from '../template/renderCarsonTemplate.js';\n\n/**\n * Project represent a self-contained piece of software (e.g. a package, or an app) that is part of a {@link Workspace}. Currently only Node.js projects are supported.\n *\n * @typeParam M If true, the project belongs to a multi-project workspace.\n */\nexport class Project<M extends boolean = true> {\n /** Project name. */\n name?: string;\n\n /** Project path. */\n path: string;\n\n /** Project path relative to workspace path. */\n relativePath: string;\n\n /** Project configuration; if it's not found, the default is an empty object. */\n config: ProjectConfig;\n\n /** Parsed project `package.json` file; if it's not found, the default is an empty object. */\n packageJson: PackageJson;\n\n /** Parent {@link Workspace} instance. */\n workspace: Workspace<M>;\n\n /** Errors found during reading from the file system. */\n errors: Array<InstanceType<typeof ProjectError>> = [];\n\n constructor({\n path: projectPath,\n name,\n config,\n packageJson,\n workspace,\n errors,\n }: ProjectOptions<M>) {\n // TODO: check that this path and workspace path are correct, i.e. project path must always be inside of workspace path\n this.path = projectPath;\n\n if (name) {\n this.name = name;\n }\n\n if (config) {\n this.config = config;\n } else {\n this.config = {};\n }\n\n if (packageJson) {\n this.packageJson = packageJson;\n } else {\n this.packageJson = {};\n }\n\n this.workspace = workspace;\n\n if (errors) {\n this.errors = errors;\n }\n\n if (this.packageJson.name?.length) {\n this.name = this.packageJson.name;\n }\n\n this.relativePath = pathToPosixPath(path.relative(this.workspace.path, this.path));\n }\n\n static async create<M extends boolean>({\n templateId,\n workspace,\n projectPath,\n projectName,\n args,\n }: ProjectCreateOptions<M>): Promise<Project<M>> {\n let project = new this({\n path: projectPath,\n name: projectName,\n config: {\n template: templateId,\n },\n workspace,\n });\n\n // if the workspace is single-project, it shares a directory with the project, so we can't insist on it being an empty directory\n if (workspace.isMultiProject) {\n try {\n await ensureEmptyDirectory(project.path);\n } catch {\n throw new ProjectError('PROJECT_PATH_NOT_EMPTY', {\n messageParameters: [project.path],\n data: {\n path: project.path,\n },\n });\n }\n }\n\n let templateRenders = await renderCarsonTemplate({\n templateId,\n templateData: {\n workspace,\n project,\n args,\n },\n });\n\n await applyTemplateRenders({\n templateRenders,\n targetPath: project.path,\n snapshotPath: path.join(project.path, CARSON_CONFIG_DIRECTORY, PROJECT_SNAPSHOT_FILENAME),\n ignoreStrategies: ['check'],\n });\n\n await saveProjectConfig({projectPath: project.path, projectConfig: {template: templateId}});\n\n await project.read();\n\n return project;\n }\n\n static async read<M extends boolean>(\n projectPath: string,\n workspace: Workspace<M>,\n ): Promise<Project<M>> {\n let project = new this({\n path: projectPath,\n workspace,\n });\n\n await project.read();\n\n return project;\n }\n\n async read() {\n if (!(await fs.pathExists(this.path))) {\n throw new ProjectError('NO_SUCH_PROJECT_PATH', {\n messageParameters: [this.path],\n data: {\n path: this.path,\n },\n });\n }\n\n let errors: Array<InstanceType<typeof ProjectError>> = [];\n\n // load project config\n let projectConfigPath = path.join(this.path, CARSON_CONFIG_DIRECTORY, PROJECT_CONFIG_FILENAME);\n let doesProjectConfigExist = await fs.pathExists(projectConfigPath);\n let projectConfig: ProjectConfig | z.ZodError;\n\n if (doesProjectConfigExist) {\n projectConfig = await readFile(projectConfigPath, projectConfigSchema, {\n parser: (rawString: string) => json5.parse<unknown>(rawString),\n });\n } else {\n projectConfig = {};\n }\n\n if (projectConfig instanceof z.ZodError) {\n let zodError = projectConfig;\n\n errors.push(\n new ProjectError('INVALID_PROJECT_CONFIG', {\n messageParameters: [projectConfigPath],\n data: {\n path: projectConfigPath,\n zodIssues: zodError.issues,\n },\n }),\n );\n\n projectConfig = {};\n }\n\n // load package.json\n let packageJsonPath = path.join(this.path, 'package.json');\n let doesPackageJsonExist = await fs.pathExists(packageJsonPath);\n let packageJson: PackageJson | z.ZodError;\n\n if (doesPackageJsonExist) {\n packageJson = await readFile(packageJsonPath, packageJsonSchema, {\n parser: (rawString: string) => json5.parse<unknown>(rawString),\n });\n } else {\n packageJson = {};\n }\n\n if (packageJson instanceof z.ZodError) {\n let zodError = packageJson;\n\n errors.push(\n new ProjectError('INVALID_PACKAGE_JSON', {\n messageParameters: [packageJsonPath],\n data: {\n path: packageJsonPath,\n zodIssues: zodError.issues,\n },\n }),\n );\n\n packageJson = {};\n }\n\n if (packageJson.name?.length) {\n this.name = packageJson.name;\n }\n\n this.config = projectConfig;\n this.packageJson = packageJson;\n this.errors = errors;\n\n return this;\n }\n\n async update({args}: ProjectUpdateOptions): Promise<this> {\n if (!this.config.template) {\n throw new ProjectError('MISSING_CARSON_TEMPLATE_ID', {\n messageParameters: [path.join(this.path, CARSON_CONFIG_DIRECTORY, PROJECT_CONFIG_FILENAME)],\n });\n }\n\n let templateRenders = await renderCarsonTemplate({\n templateId: this.config.template,\n templateData: {\n workspace: this.workspace,\n project: this,\n args,\n },\n });\n\n await applyTemplateRenders({\n templateRenders,\n targetPath: this.path,\n snapshotPath: path.join(this.path, CARSON_CONFIG_DIRECTORY, PROJECT_SNAPSHOT_FILENAME),\n ignoreStrategies: ['create'],\n });\n\n await this.read();\n\n return this;\n }\n}\n"],"names":["ensureEmptyDirectory","pathToPosixPath","readFile","packageJsonSchema","fs","json5","path","z","projectConfigSchema","ProjectError","saveProjectConfig","CARSON_CONFIG_DIRECTORY","PROJECT_CONFIG_FILENAME","PROJECT_SNAPSHOT_FILENAME","applyTemplateRenders","renderCarsonTemplate","Project","name","relativePath","config","packageJson","workspace","errors","constructor","projectPath","length","relative","create","templateId","projectName","args","project","template","isMultiProject","messageParameters","data","templateRenders","templateData","targetPath","snapshotPath","join","ignoreStrategies","projectConfig","read","pathExists","projectConfigPath","doesProjectConfigExist","parser","rawString","parse","ZodError","zodError","push","zodIssues","issues","packageJsonPath","doesPackageJsonExist","update"],"mappings":"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"}
@@ -52,6 +52,9 @@ export declare class Workspace<M extends boolean = true> {
52
52
  static read(workspacePath: string): Promise<Workspace | Workspace<false>>;
53
53
  /**
54
54
  * Traverses file system up from the search path and tries to find a directory that is a root of a workspace.
55
+ *
56
+ * @param searchPath Search path.
57
+ * @returns Workspace path, if found.
55
58
  */
56
59
  static find(searchPath: string): Promise<string | undefined>;
57
60
  /**
@@ -5,13 +5,13 @@ import fs from 'fs-extra';
5
5
  import json5 from 'json5';
6
6
  import path from 'node:path';
7
7
  import { z } from 'zod';
8
- import { compareProjectPath, containsProject, getPackageJsonWorkspaces, getRepositoryBranches, getRepositoryUrl } from './internals.js';
8
+ import { compareProjectPath, containsProject, containsWorkspace, getPackageJsonWorkspaces, getRepositoryBranches, getRepositoryUrl } from './internals.js';
9
9
  import { Project } from './Project.js';
10
10
  import { saveWorkspaceConfig } from './saveWorkspaceConfig.js';
11
11
  import { workspaceConfigSchema } from './WorkspaceConfig.js';
12
12
  import { WorkspaceDependencies } from './WorkspaceDependencies.js';
13
13
  import { WorkspaceError } from './WorkspaceError.js';
14
- import { CARSON_CONFIG_DIRECTORY, WORKSPACE_CONFIG_FILENAME } from '../constants.js';
14
+ import { CARSON_CONFIG_DIRECTORY, WORKSPACE_CONFIG_FILENAME, PACKAGE_JSON_FILENAME, WORKSPACE_SNAPSHOT_FILENAME } from '../constants.js';
15
15
  import { applyTemplateRenders } from '../template/applyTemplateRenders.js';
16
16
  import { renderCarsonTemplate } from '../template/renderCarsonTemplate.js';
17
17
  /**
@@ -130,7 +130,8 @@ import { renderCarsonTemplate } from '../template/renderCarsonTemplate.js';
130
130
  });
131
131
  await applyTemplateRenders({
132
132
  templateRenders,
133
- path: workspace.path,
133
+ targetPath: workspace.path,
134
+ snapshotPath: path.join(workspace.path, CARSON_CONFIG_DIRECTORY, WORKSPACE_SNAPSHOT_FILENAME),
134
135
  ignoreStrategies: [
135
136
  'check'
136
137
  ]
@@ -208,25 +209,31 @@ import { renderCarsonTemplate } from '../template/renderCarsonTemplate.js';
208
209
  }
209
210
  /**
210
211
  * Traverses file system up from the search path and tries to find a directory that is a root of a workspace.
212
+ *
213
+ * @param searchPath Search path.
214
+ * @returns Workspace path, if found.
211
215
  */ static async find(searchPath) {
212
216
  let startingPath = await findExistingDirectory(path.resolve(searchPath));
213
217
  let currentPath = startingPath;
214
218
  let singlePackageWorkspacePath;
215
219
  let multiPackageWorkspacePath;
216
220
  while(!(isRootPath(currentPath) || singlePackageWorkspacePath && multiPackageWorkspacePath)){
217
- let packageJsonPath = path.join(currentPath, 'package.json');
218
- let doesPackageJsonExist = await fs.pathExists(packageJsonPath);
219
- if (doesPackageJsonExist) {
220
- let packageJson = await readFile(packageJsonPath, jsonSchema, {
221
- parser: (rawString)=>json5.parse(rawString)
222
- });
223
- if (!(packageJson instanceof Error)) {
224
- let projectGlobs = getPackageJsonWorkspaces(packageJson);
225
- if (projectGlobs?.length) {
226
- multiPackageWorkspacePath ??= currentPath;
227
- } else {
228
- singlePackageWorkspacePath ??= currentPath;
221
+ if (await containsWorkspace(currentPath)) {
222
+ const packageJsonPath = path.join(currentPath, PACKAGE_JSON_FILENAME);
223
+ if (await fs.pathExists(packageJsonPath)) {
224
+ const packageJson = await readFile(packageJsonPath, jsonSchema, {
225
+ parser: (rawString)=>json5.parse(rawString)
226
+ });
227
+ if (!(packageJson instanceof Error)) {
228
+ const projectGlobs = getPackageJsonWorkspaces(packageJson);
229
+ if (projectGlobs?.length) {
230
+ multiPackageWorkspacePath ??= currentPath;
231
+ } else {
232
+ singlePackageWorkspacePath ??= currentPath;
233
+ }
229
234
  }
235
+ } else {
236
+ singlePackageWorkspacePath ??= currentPath;
230
237
  }
231
238
  }
232
239
  currentPath = path.dirname(currentPath);
@@ -372,7 +379,8 @@ import { renderCarsonTemplate } from '../template/renderCarsonTemplate.js';
372
379
  });
373
380
  await applyTemplateRenders({
374
381
  templateRenders,
375
- path: this.path,
382
+ targetPath: this.path,
383
+ snapshotPath: path.join(this.path, CARSON_CONFIG_DIRECTORY, WORKSPACE_SNAPSHOT_FILENAME),
376
384
  ignoreStrategies: [
377
385
  'create'
378
386
  ]
@@ -489,13 +497,15 @@ import { renderCarsonTemplate } from '../template/renderCarsonTemplate.js';
489
497
  }
490
498
  }
491
499
  this.projects = projects;
492
- } else {
500
+ this.sortProjects();
501
+ this.updateDependencies();
502
+ } else if (await containsProject(this.path)) {
493
503
  this.projects = [
494
504
  await Project.read(this.path, this)
495
505
  ];
506
+ this.sortProjects();
507
+ this.updateDependencies();
496
508
  }
497
- this.sortProjects();
498
- this.updateDependencies();
499
509
  return this.projects;
500
510
  }
501
511
  sortProjects() {