@jakubmazanec/carson 0.1.0-2d1145f → 0.1.0-2f12db0

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 (26) hide show
  1. package/README.md +65 -10
  2. package/dist/template/TemplateAttributes.d.ts +3 -3
  3. package/dist/template/TemplateAttributes.js +2 -1
  4. package/dist/template/TemplateAttributes.js.map +1 -1
  5. package/dist/template/applyTemplateRenders.js +22 -4
  6. package/dist/template/applyTemplateRenders.js.map +1 -1
  7. package/dist/template/internals/TemplateRenderSnapshot.d.ts +5 -5
  8. package/dist/template/internals/TemplateRenderSnapshots.d.ts +5 -5
  9. package/dist/template/internals/deleteUsingTemplateRender.d.ts +4 -1
  10. package/dist/template/internals/deleteUsingTemplateRender.js +16 -3
  11. package/dist/template/internals/deleteUsingTemplateRender.js.map +1 -1
  12. package/dist/template/internals/loadTemplateRenderSnapshots.d.ts +1 -1
  13. package/dist/template/internals/saveTemplateRenderSnapshots.js +1 -1
  14. package/dist/template/internals/saveTemplateRenderSnapshots.js.map +1 -1
  15. package/dist/template/renderCarsonTemplate.js +6 -2
  16. package/dist/template/renderCarsonTemplate.js.map +1 -1
  17. package/dist/workspace/WorkspaceCreateOptions.d.ts +1 -1
  18. package/dist/workspace/WorkspaceCreateOptions.js +1 -1
  19. package/dist/workspace/WorkspaceCreateOptions.js.map +1 -1
  20. package/package.json +9 -9
  21. package/src/template/TemplateAttributes.ts +2 -1
  22. package/src/template/applyTemplateRenders.ts +30 -3
  23. package/src/template/internals/deleteUsingTemplateRender.ts +21 -2
  24. package/src/template/internals/saveTemplateRenderSnapshots.ts +1 -0
  25. package/src/template/renderCarsonTemplate.ts +18 -2
  26. package/src/workspace/WorkspaceCreateOptions.ts +1 -1
package/README.md CHANGED
@@ -11,12 +11,26 @@ Tool for managing workspace and project configuration.
11
11
  ## Installation
12
12
 
13
13
  ```sh
14
- npm install @jakubmazanec/carson -g
14
+ npm install @jakubmazanec/carson
15
15
  ```
16
16
 
17
17
  ⚠️ This is an [ESM](https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c) package!
18
18
  It cannot be required from a CommonJS module.
19
19
 
20
+ Usually you also need a package containing Carson templates. Until you
21
+ [create your own](#carson-templates), you can use mine:
22
+
23
+ ```sh
24
+ npm install @jakubmazanec/carson-templates
25
+ ```
26
+
27
+ ⚠️ If you want to create new workspaces, you need to install both Carson and your template package
28
+ globally:
29
+
30
+ ```sh
31
+ npm install @jakubmazanec/carson @jakubmazanec/carson-templates -g
32
+ ```
33
+
20
34
  #### Prerequisites
21
35
 
22
36
  - Node.js 16 or later
@@ -176,9 +190,9 @@ See [API reference](./docs) for auto-generated documentation.
176
190
 
177
191
  ### Configuration
178
192
 
179
- Carson configuration files are located in a directory named `.carson` directory. Files named
180
- `workspace.json` and `project.json` are used to configure the workspace and project respectively (in
181
- the case of single-project workspace they're both in the same `.carson` directory).
193
+ Carson configuration files are located in a directory named `.carson`. Files named `workspace.json`
194
+ and `project.json` are used to configure the workspace and project respectively (in the case of
195
+ single-project workspace they're both in the same `.carson` directory).
182
196
 
183
197
  You can add arbitrary data to these JSONs in addition to the options described below. Because
184
198
  workspace and project configurations are available as JavaScript variables during the rendering of
@@ -269,6 +283,8 @@ there are 3 templates (for simplicity, each contains only one `*.ejs` file and a
269
283
  The package can also export some JavaScript code (defined in the `package.json` as usual); all named
270
284
  export are available as global variables during the rendering of the template files.
271
285
 
286
+ > ⚠️ Do NOT use asynchronous functions in the template files. Templates are rendered synchronously.
287
+
272
288
  #### Template files
273
289
 
274
290
  All those `*.ejs` files are parsed and rendered via `@jakubmazanec/template` package; they are
@@ -315,13 +331,15 @@ should be written at all.
315
331
 
316
332
  | Strategy name | Example `strategy` value | Description |
317
333
  | ------------- | ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
318
- | 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. |
319
- | Overwrite | `overwrite` | The template render is written to the target file, overwriting its existing content. |
320
- | 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. |
321
- | 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. |
322
- | Insert | `[insert, foobar]` | 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. |
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. |
323
340
 
324
- The rest of the template file after the front matter is the actual EJS template.
341
+ The rest of the template file after the front matter is the actual EJS template. Please read
342
+ [`ejs` documentation](https://ejs.co/#docs) to find out in more detail how they work.
325
343
 
326
344
  > ⚠️ Because EJS templates can contain arbitrary JavaScript code which Carson will run, you
327
345
  > shouldn't use Carson templates from untrusted sources! Only use templates that you've written or
@@ -339,6 +357,43 @@ Global variables available during the rendering:
339
357
 
340
358
  Currently, the config isn't used for anything yet.
341
359
 
360
+ ### Carson philosophy
361
+
362
+ The fact that Carson templates are just JavaScript gives you a lot of freedom, but it may lead you
363
+ to use Carson just as a simple config generator. Because you can add arbitrary data to the workspace
364
+ and project configs, it is tempting to use them as a source of values for "parameters" of
365
+ configurations of various tools.
366
+
367
+ For example, you can add `"useSwc": true` or `"useSwc": false` to a project config and then use this
368
+ value in a template that generates build configuration for either SWC, or Babel. But this makes
369
+ updates little bit harder, because if you later remove Babel support, you have to remember to remove
370
+ now useless setting from the project config. It also makes the logic of the Carson template more
371
+ complex; it is better to have separate templates for projects that use Babel and for project that
372
+ use SWC.
373
+
374
+ Another approach that is not recommended is using project config to specify "overrides" for various
375
+ config files:
376
+
377
+ ```json
378
+ {
379
+ "template": "@jakubmazanec/carson-templates:projects/library",
380
+
381
+ "tsconfig": {
382
+ "compilerOptions": {
383
+ "strict": false
384
+ }
385
+ }
386
+ }
387
+ ```
388
+
389
+ The `tsconfig` object is then recursively merged with the defaults during the template rendering of
390
+ the resulting `tsconfig.json`, thus allowing you to change TypeScript configuration per project.
391
+ This is not ideal, because now the Carson template isn't the only thing that defines the project
392
+ configuration, which makes updates more unpredictable.
393
+
394
+ Generally, do not push the data onto the templates, let the templates pull the data they need. Since
395
+ you create your own templates, it should be easy to do.
396
+
342
397
  ## Contributing
343
398
 
344
399
  If you want to contribute, see [CONTRIBUTING](./CONTRIBUTING.md) for details.
@@ -1,9 +1,9 @@
1
1
  import { z } from 'zod';
2
2
  export declare const templateAttributesSchema: z.ZodObject<{
3
- strategy: z.ZodUnion<[z.ZodLiteral<"create">, z.ZodLiteral<"overwrite">, z.ZodLiteral<"merge">, z.ZodLiteral<"check">, z.ZodTuple<[z.ZodLiteral<"insert">, z.ZodString], null>]>;
3
+ strategy: z.ZodUnion<[z.ZodLiteral<"create">, z.ZodLiteral<"ensure">, z.ZodLiteral<"overwrite">, z.ZodLiteral<"merge">, z.ZodLiteral<"check">, z.ZodTuple<[z.ZodLiteral<"insert">, z.ZodString], null>]>;
4
4
  }, "strict", z.ZodTypeAny, {
5
- strategy: ("create" | "overwrite" | "merge" | "check" | ["insert", string]) & ("create" | "overwrite" | "merge" | "check" | ["insert", string] | undefined);
5
+ strategy: ("create" | "ensure" | "overwrite" | "merge" | "check" | ["insert", string]) & ("create" | "ensure" | "overwrite" | "merge" | "check" | ["insert", string] | undefined);
6
6
  }, {
7
- strategy: ("create" | "overwrite" | "merge" | "check" | ["insert", string]) & ("create" | "overwrite" | "merge" | "check" | ["insert", string] | undefined);
7
+ strategy: ("create" | "ensure" | "overwrite" | "merge" | "check" | ["insert", string]) & ("create" | "ensure" | "overwrite" | "merge" | "check" | ["insert", string] | undefined);
8
8
  }>;
9
9
  export type TemplateAttributes = z.infer<typeof templateAttributesSchema>;
@@ -1,6 +1,8 @@
1
1
  import { z } from 'zod';
2
+ // TODO: add strategy 'none' so when you using template A that extends template B, you can overwrite template files to no-op
2
3
  let strategyTemplateAttributeSchema = z.union([
3
4
  z.literal('create'),
5
+ z.literal('ensure'),
4
6
  z.literal('overwrite'),
5
7
  z.literal('merge'),
6
8
  z.literal('check'),
@@ -10,7 +12,6 @@ let strategyTemplateAttributeSchema = z.union([
10
12
  ])
11
13
  ]);
12
14
  export const templateAttributesSchema = z.object({
13
- // TODO: add strategy 'none' so when you using template A that extends template B, you can overwrite template files to no-op
14
15
  strategy: strategyTemplateAttributeSchema
15
16
  }).strict();
16
17
 
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/template/TemplateAttributes.ts"],"sourcesContent":["import {z} from 'zod';\n\nlet strategyTemplateAttributeSchema = z.union([\n z.literal('create'),\n z.literal('overwrite'),\n z.literal('merge'),\n z.literal('check'),\n z.tuple([z.literal('insert'), z.string()]),\n]);\n\nexport const templateAttributesSchema = z\n .object({\n // TODO: add strategy 'none' so when you using template A that extends template B, you can overwrite template files to no-op\n strategy: strategyTemplateAttributeSchema,\n })\n .strict();\n\nexport type TemplateAttributes = z.infer<typeof templateAttributesSchema>;\n"],"names":["z","strategyTemplateAttributeSchema","union","literal","tuple","string","templateAttributesSchema","object","strategy","strict"],"mappings":"AAAA,SAAQA,CAAC,QAAO,MAAM;AAEtB,IAAIC,kCAAkCD,EAAEE,KAAK,CAAC;IAC5CF,EAAEG,OAAO,CAAC;IACVH,EAAEG,OAAO,CAAC;IACVH,EAAEG,OAAO,CAAC;IACVH,EAAEG,OAAO,CAAC;IACVH,EAAEI,KAAK,CAAC;QAACJ,EAAEG,OAAO,CAAC;QAAWH,EAAEK,MAAM;KAAG;CAC1C;AAED,OAAO,MAAMC,2BAA2BN,EACrCO,MAAM,CAAC;IACN,4HAA4H;IAC5HC,UAAUP;AACZ,GACCQ,MAAM,GAAG"}
1
+ {"version":3,"sources":["../../src/template/TemplateAttributes.ts"],"sourcesContent":["import {z} from 'zod';\n\n// TODO: add strategy 'none' so when you using template A that extends template B, you can overwrite template files to no-op\nlet strategyTemplateAttributeSchema = z.union([\n z.literal('create'),\n z.literal('ensure'),\n z.literal('overwrite'),\n z.literal('merge'),\n z.literal('check'),\n z.tuple([z.literal('insert'), z.string()]),\n]);\n\nexport const templateAttributesSchema = z\n .object({\n strategy: strategyTemplateAttributeSchema,\n })\n .strict();\n\nexport type TemplateAttributes = z.infer<typeof templateAttributesSchema>;\n"],"names":["z","strategyTemplateAttributeSchema","union","literal","tuple","string","templateAttributesSchema","object","strategy","strict"],"mappings":"AAAA,SAAQA,CAAC,QAAO,MAAM;AAEtB,4HAA4H;AAC5H,IAAIC,kCAAkCD,EAAEE,KAAK,CAAC;IAC5CF,EAAEG,OAAO,CAAC;IACVH,EAAEG,OAAO,CAAC;IACVH,EAAEG,OAAO,CAAC;IACVH,EAAEG,OAAO,CAAC;IACVH,EAAEG,OAAO,CAAC;IACVH,EAAEI,KAAK,CAAC;QAACJ,EAAEG,OAAO,CAAC;QAAWH,EAAEK,MAAM;KAAG;CAC1C;AAED,OAAO,MAAMC,2BAA2BN,EACrCO,MAAM,CAAC;IACNC,UAAUP;AACZ,GACCQ,MAAM,GAAG"}
@@ -1,4 +1,5 @@
1
- import fs from 'fs-extra';
1
+ // TODO: can this be fixed?
2
+ /* eslint-disable complexity -- TODO */ import fs from 'fs-extra';
2
3
  import path from 'node:path';
3
4
  import prettier from 'prettier';
4
5
  import { CarsonTemplateError } from './CarsonTemplateError.js';
@@ -9,6 +10,7 @@ import { CARSON_CONFIG_DIRECTORY, SNAPSHOTS_FILENAME } from '../constants.js';
9
10
  */ export async function applyTemplateRenders({ templateRenders, path: targetPath, ignoreStrategies }) {
10
11
  // we need to clear Prettier cache, because we are changing file system
11
12
  prettier.clearConfigCache();
13
+ // first, we read the existing snapshots and use them to undo previous changes
12
14
  let templateRenderSnapshotsPath = path.join(targetPath, CARSON_CONFIG_DIRECTORY, SNAPSHOTS_FILENAME);
13
15
  if (await fs.pathExists(templateRenderSnapshotsPath)) {
14
16
  let templateRenderSnapshots = await loadTemplateRenderSnapshots(templateRenderSnapshotsPath);
@@ -17,6 +19,11 @@ import { CARSON_CONFIG_DIRECTORY, SNAPSHOTS_FILENAME } from '../constants.js';
17
19
  if (ignoreStrategies.includes(strategy)) {
18
20
  continue;
19
21
  }
22
+ if (templateRenderSnapshot.attributes.strategy === 'ensure') {
23
+ await deleteUsingTemplateRender(templateRenderSnapshot, targetPath, {
24
+ onlyDeleteWhenContentsMatch: true
25
+ });
26
+ }
20
27
  if (templateRenderSnapshot.attributes.strategy === 'overwrite') {
21
28
  await deleteUsingTemplateRender(templateRenderSnapshot, targetPath);
22
29
  }
@@ -28,13 +35,24 @@ import { CARSON_CONFIG_DIRECTORY, SNAPSHOTS_FILENAME } from '../constants.js';
28
35
  }
29
36
  }
30
37
  }
31
- if (!ignoreStrategies.includes('create')) {
32
- for (let templateRender of templateRenders){
33
- if (templateRender.attributes.strategy === 'create') {
38
+ // second, we apply strategies that create files
39
+ for (let templateRender of templateRenders){
40
+ let strategy = typeof templateRender.attributes.strategy === 'string' ? templateRender.attributes.strategy : templateRender.attributes.strategy[0];
41
+ if (ignoreStrategies.includes(strategy)) {
42
+ continue;
43
+ }
44
+ if (templateRender.attributes.strategy === 'create') {
45
+ await createUsingTemplateRender(templateRender, targetPath);
46
+ }
47
+ if (templateRender.attributes.strategy === 'ensure') {
48
+ let to = path.join(targetPath, templateRender.attributes.to);
49
+ let doesToExist = await fs.pathExists(to);
50
+ if (!doesToExist) {
34
51
  await createUsingTemplateRender(templateRender, targetPath);
35
52
  }
36
53
  }
37
54
  }
55
+ // third, we apply strategies that change files
38
56
  for (let templateRender of templateRenders){
39
57
  let strategy = typeof templateRender.attributes.strategy === 'string' ? templateRender.attributes.strategy : templateRender.attributes.strategy[0];
40
58
  if (ignoreStrategies.includes(strategy)) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/template/applyTemplateRenders.ts"],"sourcesContent":["import 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 prettier.clearConfigCache();\n\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 === '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 if (!ignoreStrategies.includes('create')) {\n for (let templateRender of templateRenders) {\n if (templateRender.attributes.strategy === 'create') {\n await createUsingTemplateRender(templateRender, targetPath);\n }\n }\n }\n\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 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","templateRender","to","doesToExist","messageParameters","template","data"],"mappings":"AAAA,OAAOA,QAAQ,WAAW;AAC1B,OAAOC,UAAU,YAAY;AAC7B,OAAOC,cAAc,WAAW;AAEhC,SAAQC,mBAAmB,QAAO,2BAA2B;AAC7D,SACEC,yBAAyB,EACzBC,yBAAyB,EACzBC,yBAAyB,EACzBC,2BAA2B,EAC3BC,wBAAwB,EACxBC,4BAA4B,EAC5BC,2BAA2B,EAC3BC,2BAA2B,EAC3BC,0BAA0B,QACrB,iBAAiB;AAExB,SAAQC,uBAAuB,EAAEC,kBAAkB,QAAO,kBAAkB;AAQ5E;;CAEC,GACD,OAAO,eAAeC,qBAAqB,EACzCC,eAAe,EACff,MAAMgB,UAAU,EAChBC,gBAAgB,EACY;IAC5B,uEAAuE;IACvEhB,SAASiB,gBAAgB;IAEzB,IAAIC,8BAA8BnB,KAAKoB,IAAI,CACzCJ,YACAJ,yBACAC;IAGF,IAAI,MAAMd,GAAGsB,UAAU,CAACF,8BAA8B;QACpD,IAAIG,0BAA0B,MAAMhB,4BAA4Ba;QAEhE,KAAK,IAAII,0BAA0BD,wBAAyB;YAC1D,IAAIE,WACF,OAAOD,uBAAuBE,UAAU,CAACD,QAAQ,KAAK,WAClDD,uBAAuBE,UAAU,CAACD,QAAQ,GAC1CD,uBAAuBE,UAAU,CAACD,QAAQ,CAAC,EAAE;YAEnD,IAAIP,iBAAiBS,QAAQ,CAACF,WAAW;gBACvC;YACF;YAEA,IAAID,uBAAuBE,UAAU,CAACD,QAAQ,KAAK,aAAa;gBAC9D,MAAMpB,0BAA0BmB,wBAAwBP;YAC1D;YAEA,IAAIO,uBAAuBE,UAAU,CAACD,QAAQ,KAAK,SAAS;gBAC1D,MAAMb,2BAA2BY,wBAAwBP;YAC3D;YAEA,IAAIO,uBAAuBE,UAAU,CAACD,QAAQ,CAAC,EAAE,KAAK,UAAU;gBAC9D,MAAMd,4BAA4Ba,wBAAwBP;YAC5D;QACF;IACF;IAEA,IAAI,CAACC,iBAAiBS,QAAQ,CAAC,WAAW;QACxC,KAAK,IAAIC,kBAAkBZ,gBAAiB;YAC1C,IAAIY,eAAeF,UAAU,CAACD,QAAQ,KAAK,UAAU;gBACnD,MAAMrB,0BAA0BwB,gBAAgBX;YAClD;QACF;IACF;IAEA,KAAK,IAAIW,kBAAkBZ,gBAAiB;QAC1C,IAAIS,WACF,OAAOG,eAAeF,UAAU,CAACD,QAAQ,KAAK,WAC1CG,eAAeF,UAAU,CAACD,QAAQ,GAClCG,eAAeF,UAAU,CAACD,QAAQ,CAAC,EAAE;QAE3C,IAAIP,iBAAiBS,QAAQ,CAACF,WAAW;YACvC;QACF;QAEA,IAAIG,eAAeF,UAAU,CAACD,QAAQ,KAAK,aAAa;YACtD,MAAMhB,6BAA6BmB,gBAAgBX;QACrD;QAEA,IAAIW,eAAeF,UAAU,CAACD,QAAQ,KAAK,SAAS;YAClD,MAAMjB,yBAAyBoB,gBAAgBX;QACjD;QAEA,IAAIW,eAAeF,UAAU,CAACD,QAAQ,CAAC,EAAE,KAAK,UAAU;YACtD,MAAMnB,0BAA0BsB,gBAAgBX;QAClD;QAEA,IAAIW,eAAeF,UAAU,CAACD,QAAQ,KAAK,SAAS;YAClD,IAAII,KAAK5B,KAAKoB,IAAI,CAACJ,YAAYW,eAAeF,UAAU,CAACG,EAAE;YAC3D,IAAIC,cAAc,MAAM9B,GAAGsB,UAAU,CAACO;YAEtC,IAAI,CAACC,aAAa;gBAChB,MAAM,IAAI3B,oBAAoB,yBAAyB;oBACrD4B,mBAAmB;wBAACH,eAAeF,UAAU,CAACD,QAAQ;wBAAEG,eAAeI,QAAQ,CAAC/B,IAAI;wBAAE4B;qBAAG;oBACzFI,MAAM;wBACJhC,MAAM4B;oBACR;gBACF;YACF;QACF;IACF;IAEA,MAAMnB,4BAA4BM,iBAAiBI;AACrD"}
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 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 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"],"mappings":"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"}
@@ -2,12 +2,12 @@ import { z } from 'zod';
2
2
  export declare const templateRenderSnapshotSchema: z.ZodObject<{
3
3
  attributes: z.ZodObject<{
4
4
  to: z.ZodString;
5
- strategy: z.ZodUnion<[z.ZodLiteral<"create">, z.ZodLiteral<"overwrite">, z.ZodLiteral<"merge">, z.ZodLiteral<"check">, z.ZodTuple<[z.ZodLiteral<"insert">, z.ZodString], null>]>;
5
+ strategy: z.ZodUnion<[z.ZodLiteral<"create">, z.ZodLiteral<"ensure">, z.ZodLiteral<"overwrite">, z.ZodLiteral<"merge">, z.ZodLiteral<"check">, z.ZodTuple<[z.ZodLiteral<"insert">, z.ZodString], null>]>;
6
6
  }, "strict", z.ZodTypeAny, {
7
- strategy: ("create" | "overwrite" | "merge" | "check" | ["insert", string]) & ("create" | "overwrite" | "merge" | "check" | ["insert", string] | undefined);
7
+ strategy: ("create" | "ensure" | "overwrite" | "merge" | "check" | ["insert", string]) & ("create" | "ensure" | "overwrite" | "merge" | "check" | ["insert", string] | undefined);
8
8
  to: string;
9
9
  }, {
10
- strategy: ("create" | "overwrite" | "merge" | "check" | ["insert", string]) & ("create" | "overwrite" | "merge" | "check" | ["insert", string] | undefined);
10
+ strategy: ("create" | "ensure" | "overwrite" | "merge" | "check" | ["insert", string]) & ("create" | "ensure" | "overwrite" | "merge" | "check" | ["insert", string] | undefined);
11
11
  to: string;
12
12
  }>;
13
13
  content: z.ZodString;
@@ -23,7 +23,7 @@ export declare const templateRenderSnapshotSchema: z.ZodObject<{
23
23
  path: string;
24
24
  };
25
25
  attributes: {
26
- strategy: ("create" | "overwrite" | "merge" | "check" | ["insert", string]) & ("create" | "overwrite" | "merge" | "check" | ["insert", string] | undefined);
26
+ strategy: ("create" | "ensure" | "overwrite" | "merge" | "check" | ["insert", string]) & ("create" | "ensure" | "overwrite" | "merge" | "check" | ["insert", string] | undefined);
27
27
  to: string;
28
28
  };
29
29
  content: string;
@@ -32,7 +32,7 @@ export declare const templateRenderSnapshotSchema: z.ZodObject<{
32
32
  path: string;
33
33
  };
34
34
  attributes: {
35
- strategy: ("create" | "overwrite" | "merge" | "check" | ["insert", string]) & ("create" | "overwrite" | "merge" | "check" | ["insert", string] | undefined);
35
+ strategy: ("create" | "ensure" | "overwrite" | "merge" | "check" | ["insert", string]) & ("create" | "ensure" | "overwrite" | "merge" | "check" | ["insert", string] | undefined);
36
36
  to: string;
37
37
  };
38
38
  content: string;
@@ -2,12 +2,12 @@ import { type z } from 'zod';
2
2
  export declare const templateRenderSnapshotsSchema: z.ZodArray<z.ZodObject<{
3
3
  attributes: z.ZodObject<{
4
4
  to: z.ZodString;
5
- strategy: z.ZodUnion<[z.ZodLiteral<"create">, z.ZodLiteral<"overwrite">, z.ZodLiteral<"merge">, z.ZodLiteral<"check">, z.ZodTuple<[z.ZodLiteral<"insert">, z.ZodString], null>]>;
5
+ strategy: z.ZodUnion<[z.ZodLiteral<"create">, z.ZodLiteral<"ensure">, z.ZodLiteral<"overwrite">, z.ZodLiteral<"merge">, z.ZodLiteral<"check">, z.ZodTuple<[z.ZodLiteral<"insert">, z.ZodString], null>]>;
6
6
  }, "strict", z.ZodTypeAny, {
7
- strategy: ("create" | "overwrite" | "merge" | "check" | ["insert", string]) & ("create" | "overwrite" | "merge" | "check" | ["insert", string] | undefined);
7
+ strategy: ("create" | "ensure" | "overwrite" | "merge" | "check" | ["insert", string]) & ("create" | "ensure" | "overwrite" | "merge" | "check" | ["insert", string] | undefined);
8
8
  to: string;
9
9
  }, {
10
- strategy: ("create" | "overwrite" | "merge" | "check" | ["insert", string]) & ("create" | "overwrite" | "merge" | "check" | ["insert", string] | undefined);
10
+ strategy: ("create" | "ensure" | "overwrite" | "merge" | "check" | ["insert", string]) & ("create" | "ensure" | "overwrite" | "merge" | "check" | ["insert", string] | undefined);
11
11
  to: string;
12
12
  }>;
13
13
  content: z.ZodString;
@@ -23,7 +23,7 @@ export declare const templateRenderSnapshotsSchema: z.ZodArray<z.ZodObject<{
23
23
  path: string;
24
24
  };
25
25
  attributes: {
26
- strategy: ("create" | "overwrite" | "merge" | "check" | ["insert", string]) & ("create" | "overwrite" | "merge" | "check" | ["insert", string] | undefined);
26
+ strategy: ("create" | "ensure" | "overwrite" | "merge" | "check" | ["insert", string]) & ("create" | "ensure" | "overwrite" | "merge" | "check" | ["insert", string] | undefined);
27
27
  to: string;
28
28
  };
29
29
  content: string;
@@ -32,7 +32,7 @@ export declare const templateRenderSnapshotsSchema: z.ZodArray<z.ZodObject<{
32
32
  path: string;
33
33
  };
34
34
  attributes: {
35
- strategy: ("create" | "overwrite" | "merge" | "check" | ["insert", string]) & ("create" | "overwrite" | "merge" | "check" | ["insert", string] | undefined);
35
+ strategy: ("create" | "ensure" | "overwrite" | "merge" | "check" | ["insert", string]) & ("create" | "ensure" | "overwrite" | "merge" | "check" | ["insert", string] | undefined);
36
36
  to: string;
37
37
  };
38
38
  content: string;
@@ -1,3 +1,6 @@
1
1
  import { type TemplateRenderSnapshot } from './TemplateRenderSnapshot.js';
2
2
  import { type TemplateRender } from '../TemplateRender.js';
3
- export declare function deleteUsingTemplateRender(templateRender: TemplateRender | TemplateRenderSnapshot, basePath: string): Promise<void>;
3
+ export type DeleteUsingTemplateRenderOptions = {
4
+ onlyDeleteWhenContentsMatch?: boolean;
5
+ };
6
+ export declare function deleteUsingTemplateRender(templateRender: TemplateRender | TemplateRenderSnapshot, basePath: string, options?: DeleteUsingTemplateRenderOptions): Promise<void>;
@@ -1,10 +1,23 @@
1
1
  import fs from 'fs-extra';
2
2
  import path from 'node:path';
3
- export async function deleteUsingTemplateRender(templateRender, basePath) {
3
+ import { prettify } from './prettify.js';
4
+ export async function deleteUsingTemplateRender(templateRender, basePath, options) {
5
+ var _options;
4
6
  let to = path.join(basePath, templateRender.attributes.to);
5
- if (await fs.pathExists(to)) {
6
- await fs.rm(to);
7
+ if (!await fs.pathExists(to)) {
8
+ return;
7
9
  }
10
+ if ((_options = options) === null || _options === void 0 ? void 0 : _options.onlyDeleteWhenContentsMatch) {
11
+ let existingContent = await fs.readFile(to, {
12
+ encoding: 'utf8'
13
+ });
14
+ let newContent = await prettify(templateRender.content, to);
15
+ if (existingContent.trim() === newContent.trim()) {
16
+ await fs.rm(to);
17
+ }
18
+ return;
19
+ }
20
+ await fs.rm(to);
8
21
  }
9
22
 
10
23
  //# sourceMappingURL=deleteUsingTemplateRender.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/template/internals/deleteUsingTemplateRender.ts"],"sourcesContent":["import fs from 'fs-extra';\nimport path from 'node:path';\n\nimport {type TemplateRenderSnapshot} from './TemplateRenderSnapshot.js';\nimport {type TemplateRender} from '../TemplateRender.js';\n\nexport async function deleteUsingTemplateRender(\n templateRender: TemplateRender | TemplateRenderSnapshot,\n basePath: string,\n) {\n let to = path.join(basePath, templateRender.attributes.to);\n\n if (await fs.pathExists(to)) {\n await fs.rm(to);\n }\n}\n"],"names":["fs","path","deleteUsingTemplateRender","templateRender","basePath","to","join","attributes","pathExists","rm"],"mappings":"AAAA,OAAOA,QAAQ,WAAW;AAC1B,OAAOC,UAAU,YAAY;AAK7B,OAAO,eAAeC,0BACpBC,cAAuD,EACvDC,QAAgB;IAEhB,IAAIC,KAAKJ,KAAKK,IAAI,CAACF,UAAUD,eAAeI,UAAU,CAACF,EAAE;IAEzD,IAAI,MAAML,GAAGQ,UAAU,CAACH,KAAK;QAC3B,MAAML,GAAGS,EAAE,CAACJ;IACd;AACF"}
1
+ {"version":3,"sources":["../../../src/template/internals/deleteUsingTemplateRender.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 type DeleteUsingTemplateRenderOptions = {\n onlyDeleteWhenContentsMatch?: boolean;\n};\n\nexport async function deleteUsingTemplateRender(\n templateRender: TemplateRender | TemplateRenderSnapshot,\n basePath: string,\n options?: DeleteUsingTemplateRenderOptions,\n) {\n let to = path.join(basePath, templateRender.attributes.to);\n\n if (!(await fs.pathExists(to))) {\n return;\n }\n\n if (options?.onlyDeleteWhenContentsMatch) {\n let existingContent = await fs.readFile(to, {encoding: 'utf8'});\n let newContent = await prettify(templateRender.content, to);\n\n if (existingContent.trim() === newContent.trim()) {\n await fs.rm(to);\n }\n\n return;\n }\n\n await fs.rm(to);\n}\n"],"names":["fs","path","prettify","deleteUsingTemplateRender","templateRender","basePath","options","to","join","attributes","pathExists","onlyDeleteWhenContentsMatch","existingContent","readFile","encoding","newContent","content","trim","rm"],"mappings":"AAAA,OAAOA,QAAQ,WAAW;AAC1B,OAAOC,UAAU,YAAY;AAE7B,SAAQC,QAAQ,QAAO,gBAAgB;AAQvC,OAAO,eAAeC,0BACpBC,cAAuD,EACvDC,QAAgB,EAChBC,OAA0C;QAQtCA;IANJ,IAAIC,KAAKN,KAAKO,IAAI,CAACH,UAAUD,eAAeK,UAAU,CAACF,EAAE;IAEzD,IAAI,CAAE,MAAMP,GAAGU,UAAU,CAACH,KAAM;QAC9B;IACF;IAEA,KAAID,WAAAA,qBAAAA,+BAAAA,SAASK,2BAA2B,EAAE;QACxC,IAAIC,kBAAkB,MAAMZ,GAAGa,QAAQ,CAACN,IAAI;YAACO,UAAU;QAAM;QAC7D,IAAIC,aAAa,MAAMb,SAASE,eAAeY,OAAO,EAAET;QAExD,IAAIK,gBAAgBK,IAAI,OAAOF,WAAWE,IAAI,IAAI;YAChD,MAAMjB,GAAGkB,EAAE,CAACX;QACd;QAEA;IACF;IAEA,MAAMP,GAAGkB,EAAE,CAACX;AACd"}
@@ -3,7 +3,7 @@ export declare function loadTemplateRenderSnapshots(templateRenderSnapshotsPath:
3
3
  path: string;
4
4
  };
5
5
  attributes: {
6
- strategy: ("create" | "overwrite" | "merge" | "check" | ["insert", string]) & ("create" | "overwrite" | "merge" | "check" | ["insert", string] | undefined);
6
+ strategy: ("create" | "ensure" | "overwrite" | "merge" | "check" | ["insert", string]) & ("create" | "ensure" | "overwrite" | "merge" | "check" | ["insert", string] | undefined);
7
7
  to: string;
8
8
  };
9
9
  content: string;
@@ -3,7 +3,7 @@ import fs from 'fs-extra';
3
3
  export async function saveTemplateRenderSnapshots(templateRenders, templateRenderSnapshotsPath) {
4
4
  let templateRendersSnapshots = [];
5
5
  for (let templateRender of templateRenders){
6
- if (templateRender.attributes.strategy === 'overwrite' || templateRender.attributes.strategy === 'merge' || templateRender.attributes.strategy[0] === 'insert') {
6
+ if (templateRender.attributes.strategy === 'ensure' || templateRender.attributes.strategy === 'overwrite' || templateRender.attributes.strategy === 'merge' || templateRender.attributes.strategy[0] === 'insert') {
7
7
  templateRendersSnapshots.push({
8
8
  attributes: {
9
9
  to: templateRender.attributes.to,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/template/internals/saveTemplateRenderSnapshots.ts"],"sourcesContent":["import {deflateSync, strToU8} from 'fflate';\nimport fs from 'fs-extra';\n\nimport {type TemplateRenderSnapshots} from './TemplateRenderSnapshots.js';\nimport {type TemplateRenders} from '../TemplateRenders.js';\n\nexport async function saveTemplateRenderSnapshots(\n templateRenders: TemplateRenders,\n templateRenderSnapshotsPath: string,\n) {\n let templateRendersSnapshots: TemplateRenderSnapshots = [];\n\n for (let templateRender of templateRenders) {\n if (\n templateRender.attributes.strategy === 'overwrite' ||\n templateRender.attributes.strategy === 'merge' ||\n templateRender.attributes.strategy[0] === 'insert'\n ) {\n templateRendersSnapshots.push({\n attributes: {\n to: templateRender.attributes.to,\n strategy: templateRender.attributes.strategy,\n },\n content: templateRender.content,\n template: templateRender.template,\n });\n }\n }\n\n await fs.writeFile(\n templateRenderSnapshotsPath,\n deflateSync(strToU8(JSON.stringify(templateRendersSnapshots)), {level: 1, mem: 0}),\n );\n}\n"],"names":["deflateSync","strToU8","fs","saveTemplateRenderSnapshots","templateRenders","templateRenderSnapshotsPath","templateRendersSnapshots","templateRender","attributes","strategy","push","to","content","template","writeFile","JSON","stringify","level","mem"],"mappings":"AAAA,SAAQA,WAAW,EAAEC,OAAO,QAAO,SAAS;AAC5C,OAAOC,QAAQ,WAAW;AAK1B,OAAO,eAAeC,4BACpBC,eAAgC,EAChCC,2BAAmC;IAEnC,IAAIC,2BAAoD,EAAE;IAE1D,KAAK,IAAIC,kBAAkBH,gBAAiB;QAC1C,IACEG,eAAeC,UAAU,CAACC,QAAQ,KAAK,eACvCF,eAAeC,UAAU,CAACC,QAAQ,KAAK,WACvCF,eAAeC,UAAU,CAACC,QAAQ,CAAC,EAAE,KAAK,UAC1C;YACAH,yBAAyBI,IAAI,CAAC;gBAC5BF,YAAY;oBACVG,IAAIJ,eAAeC,UAAU,CAACG,EAAE;oBAChCF,UAAUF,eAAeC,UAAU,CAACC,QAAQ;gBAC9C;gBACAG,SAASL,eAAeK,OAAO;gBAC/BC,UAAUN,eAAeM,QAAQ;YACnC;QACF;IACF;IAEA,MAAMX,GAAGY,SAAS,CAChBT,6BACAL,YAAYC,QAAQc,KAAKC,SAAS,CAACV,4BAA4B;QAACW,OAAO;QAAGC,KAAK;IAAC;AAEpF"}
1
+ {"version":3,"sources":["../../../src/template/internals/saveTemplateRenderSnapshots.ts"],"sourcesContent":["import {deflateSync, strToU8} from 'fflate';\nimport fs from 'fs-extra';\n\nimport {type TemplateRenderSnapshots} from './TemplateRenderSnapshots.js';\nimport {type TemplateRenders} from '../TemplateRenders.js';\n\nexport async function saveTemplateRenderSnapshots(\n templateRenders: TemplateRenders,\n templateRenderSnapshotsPath: string,\n) {\n let templateRendersSnapshots: TemplateRenderSnapshots = [];\n\n for (let templateRender of templateRenders) {\n if (\n templateRender.attributes.strategy === 'ensure' ||\n templateRender.attributes.strategy === 'overwrite' ||\n templateRender.attributes.strategy === 'merge' ||\n templateRender.attributes.strategy[0] === 'insert'\n ) {\n templateRendersSnapshots.push({\n attributes: {\n to: templateRender.attributes.to,\n strategy: templateRender.attributes.strategy,\n },\n content: templateRender.content,\n template: templateRender.template,\n });\n }\n }\n\n await fs.writeFile(\n templateRenderSnapshotsPath,\n deflateSync(strToU8(JSON.stringify(templateRendersSnapshots)), {level: 1, mem: 0}),\n );\n}\n"],"names":["deflateSync","strToU8","fs","saveTemplateRenderSnapshots","templateRenders","templateRenderSnapshotsPath","templateRendersSnapshots","templateRender","attributes","strategy","push","to","content","template","writeFile","JSON","stringify","level","mem"],"mappings":"AAAA,SAAQA,WAAW,EAAEC,OAAO,QAAO,SAAS;AAC5C,OAAOC,QAAQ,WAAW;AAK1B,OAAO,eAAeC,4BACpBC,eAAgC,EAChCC,2BAAmC;IAEnC,IAAIC,2BAAoD,EAAE;IAE1D,KAAK,IAAIC,kBAAkBH,gBAAiB;QAC1C,IACEG,eAAeC,UAAU,CAACC,QAAQ,KAAK,YACvCF,eAAeC,UAAU,CAACC,QAAQ,KAAK,eACvCF,eAAeC,UAAU,CAACC,QAAQ,KAAK,WACvCF,eAAeC,UAAU,CAACC,QAAQ,CAAC,EAAE,KAAK,UAC1C;YACAH,yBAAyBI,IAAI,CAAC;gBAC5BF,YAAY;oBACVG,IAAIJ,eAAeC,UAAU,CAACG,EAAE;oBAChCF,UAAUF,eAAeC,UAAU,CAACC,QAAQ;gBAC9C;gBACAG,SAASL,eAAeK,OAAO;gBAC/BC,UAAUN,eAAeM,QAAQ;YACnC;QACF;IACF;IAEA,MAAMX,GAAGY,SAAS,CAChBT,6BACAL,YAAYC,QAAQc,KAAKC,SAAS,CAACV,4BAA4B;QAACW,OAAO;QAAGC,KAAK;IAAC;AAEpF"}
@@ -1,8 +1,12 @@
1
+ import { pathToPosixPath } from '@jakubmazanec/fs-utils';
1
2
  import _ from 'lodash';
2
- import { importCarsonTemplate, readCarsonTemplate } from './internals.js';
3
+ import path from 'node:path';
4
+ import { importCarsonTemplate, parseCarsonTemplateId, readCarsonTemplate, resolveModule } from './internals.js';
3
5
  /**
4
6
  * Renders a Carson template with provided template data.
5
7
  */ export async function renderCarsonTemplate(options) {
8
+ let [moduleId] = parseCarsonTemplateId(options.templateId);
9
+ let modulePath = await resolveModule(moduleId);
6
10
  let template = await readCarsonTemplate(options.templateId);
7
11
  let templateModule = await importCarsonTemplate(options.templateId);
8
12
  let templateRenders = [];
@@ -15,7 +19,7 @@ import { importCarsonTemplate, readCarsonTemplate } from './internals.js';
15
19
  })).map((templateRender)=>({
16
20
  ...templateRender,
17
21
  template: {
18
- path: file.path ?? ''
22
+ path: pathToPosixPath(`${moduleId}/${path.relative(modulePath, file.path ?? modulePath)}`)
19
23
  }
20
24
  })));
21
25
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/template/renderCarsonTemplate.ts"],"sourcesContent":["import _ from 'lodash';\n\nimport {importCarsonTemplate, readCarsonTemplate} from './internals.js';\nimport {type TemplateData} from './TemplateData.js';\nimport {type TemplateRenders} from './TemplateRenders.js';\n\nexport type RenderCarsonTemplateOptions = {\n templateId: string;\n templateData: TemplateData;\n};\n\n/**\n * Renders a Carson template with provided template data.\n */\nexport async function renderCarsonTemplate(\n options: RenderCarsonTemplateOptions,\n): Promise<TemplateRenders> {\n let template = await readCarsonTemplate(options.templateId);\n let templateModule = await importCarsonTemplate(options.templateId);\n let templateRenders: TemplateRenders = [];\n\n for (let file of template.files) {\n templateRenders.push(\n ...(\n await file.render(\n {\n ...templateModule,\n ...options.templateData,\n },\n {disablePrettier: true},\n )\n ).map((templateRender) => ({...templateRender, template: {path: file.path ?? ''}})),\n );\n }\n\n templateRenders = _.sortBy(templateRenders, [\n (templateRender) => templateRender.attributes.to,\n (templateRender) => templateRender.attributes.strategy,\n (templateRender) => templateRender.content,\n ]);\n\n return templateRenders;\n}\n"],"names":["_","importCarsonTemplate","readCarsonTemplate","renderCarsonTemplate","options","template","templateId","templateModule","templateRenders","file","files","push","render","templateData","disablePrettier","map","templateRender","path","sortBy","attributes","to","strategy","content"],"mappings":"AAAA,OAAOA,OAAO,SAAS;AAEvB,SAAQC,oBAAoB,EAAEC,kBAAkB,QAAO,iBAAiB;AASxE;;CAEC,GACD,OAAO,eAAeC,qBACpBC,OAAoC;IAEpC,IAAIC,WAAW,MAAMH,mBAAmBE,QAAQE,UAAU;IAC1D,IAAIC,iBAAiB,MAAMN,qBAAqBG,QAAQE,UAAU;IAClE,IAAIE,kBAAmC,EAAE;IAEzC,KAAK,IAAIC,QAAQJ,SAASK,KAAK,CAAE;QAC/BF,gBAAgBG,IAAI,IACf,AACD,CAAA,MAAMF,KAAKG,MAAM,CACf;YACE,GAAGL,cAAc;YACjB,GAAGH,QAAQS,YAAY;QACzB,GACA;YAACC,iBAAiB;QAAI,EACxB,EACAC,GAAG,CAAC,CAACC,iBAAoB,CAAA;gBAAC,GAAGA,cAAc;gBAAEX,UAAU;oBAACY,MAAMR,KAAKQ,IAAI,IAAI;gBAAE;YAAC,CAAA;IAEpF;IAEAT,kBAAkBR,EAAEkB,MAAM,CAACV,iBAAiB;QAC1C,CAACQ,iBAAmBA,eAAeG,UAAU,CAACC,EAAE;QAChD,CAACJ,iBAAmBA,eAAeG,UAAU,CAACE,QAAQ;QACtD,CAACL,iBAAmBA,eAAeM,OAAO;KAC3C;IAED,OAAOd;AACT"}
1
+ {"version":3,"sources":["../../src/template/renderCarsonTemplate.ts"],"sourcesContent":["import {pathToPosixPath} from '@jakubmazanec/fs-utils';\nimport _ from 'lodash';\nimport path from 'node:path';\n\nimport {\n importCarsonTemplate,\n parseCarsonTemplateId,\n readCarsonTemplate,\n resolveModule,\n} from './internals.js';\nimport {type TemplateData} from './TemplateData.js';\nimport {type TemplateRenders} from './TemplateRenders.js';\n\nexport type RenderCarsonTemplateOptions = {\n templateId: string;\n templateData: TemplateData;\n};\n\n/**\n * Renders a Carson template with provided template data.\n */\nexport async function renderCarsonTemplate(\n options: RenderCarsonTemplateOptions,\n): Promise<TemplateRenders> {\n let [moduleId] = parseCarsonTemplateId(options.templateId);\n let modulePath = await resolveModule(moduleId);\n let template = await readCarsonTemplate(options.templateId);\n let templateModule = await importCarsonTemplate(options.templateId);\n let templateRenders: TemplateRenders = [];\n\n for (let file of template.files) {\n templateRenders.push(\n ...(\n await file.render(\n {\n ...templateModule,\n ...options.templateData,\n },\n {disablePrettier: true},\n )\n ).map((templateRender) => ({\n ...templateRender,\n template: {\n path: pathToPosixPath(\n `${moduleId}/${path.relative(modulePath, file.path ?? modulePath)}`,\n ),\n },\n })),\n );\n }\n\n templateRenders = _.sortBy(templateRenders, [\n (templateRender) => templateRender.attributes.to,\n (templateRender) => templateRender.attributes.strategy,\n (templateRender) => templateRender.content,\n ]);\n\n return templateRenders;\n}\n"],"names":["pathToPosixPath","_","path","importCarsonTemplate","parseCarsonTemplateId","readCarsonTemplate","resolveModule","renderCarsonTemplate","options","moduleId","templateId","modulePath","template","templateModule","templateRenders","file","files","push","render","templateData","disablePrettier","map","templateRender","relative","sortBy","attributes","to","strategy","content"],"mappings":"AAAA,SAAQA,eAAe,QAAO,yBAAyB;AACvD,OAAOC,OAAO,SAAS;AACvB,OAAOC,UAAU,YAAY;AAE7B,SACEC,oBAAoB,EACpBC,qBAAqB,EACrBC,kBAAkB,EAClBC,aAAa,QACR,iBAAiB;AASxB;;CAEC,GACD,OAAO,eAAeC,qBACpBC,OAAoC;IAEpC,IAAI,CAACC,SAAS,GAAGL,sBAAsBI,QAAQE,UAAU;IACzD,IAAIC,aAAa,MAAML,cAAcG;IACrC,IAAIG,WAAW,MAAMP,mBAAmBG,QAAQE,UAAU;IAC1D,IAAIG,iBAAiB,MAAMV,qBAAqBK,QAAQE,UAAU;IAClE,IAAII,kBAAmC,EAAE;IAEzC,KAAK,IAAIC,QAAQH,SAASI,KAAK,CAAE;QAC/BF,gBAAgBG,IAAI,IACf,AACD,CAAA,MAAMF,KAAKG,MAAM,CACf;YACE,GAAGL,cAAc;YACjB,GAAGL,QAAQW,YAAY;QACzB,GACA;YAACC,iBAAiB;QAAI,EACxB,EACAC,GAAG,CAAC,CAACC,iBAAoB,CAAA;gBACzB,GAAGA,cAAc;gBACjBV,UAAU;oBACRV,MAAMF,gBACJ,CAAC,EAAES,SAAS,CAAC,EAAEP,KAAKqB,QAAQ,CAACZ,YAAYI,KAAKb,IAAI,IAAIS,YAAY,CAAC;gBAEvE;YACF,CAAA;IAEJ;IAEAG,kBAAkBb,EAAEuB,MAAM,CAACV,iBAAiB;QAC1C,CAACQ,iBAAmBA,eAAeG,UAAU,CAACC,EAAE;QAChD,CAACJ,iBAAmBA,eAAeG,UAAU,CAACE,QAAQ;QACtD,CAACL,iBAAmBA,eAAeM,OAAO;KAC3C;IAED,OAAOd;AACT"}
@@ -1,5 +1,5 @@
1
1
  /**
2
- * {@link Workspace.create | Workspace.create} options parameter.
2
+ * {@link Workspace.create} options parameter.
3
3
  */
4
4
  export type WorkspaceCreateOptions = {
5
5
  path: string;
@@ -1,5 +1,5 @@
1
1
  /**
2
- * {@link Workspace.create | Workspace.create} options parameter.
2
+ * {@link Workspace.create} options parameter.
3
3
  */ export { };
4
4
 
5
5
  //# sourceMappingURL=WorkspaceCreateOptions.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/workspace/WorkspaceCreateOptions.ts"],"sourcesContent":["/**\n * {@link Workspace.create | Workspace.create} options parameter.\n */\nexport type WorkspaceCreateOptions = {\n path: string;\n templateId: string;\n args: Record<string, unknown>;\n};\n"],"names":[],"mappings":"AAAA;;CAEC,GACD,WAIE"}
1
+ {"version":3,"sources":["../../src/workspace/WorkspaceCreateOptions.ts"],"sourcesContent":["/**\n * {@link Workspace.create} options parameter.\n */\nexport type WorkspaceCreateOptions = {\n path: string;\n templateId: string;\n args: Record<string, unknown>;\n};\n"],"names":[],"mappings":"AAAA;;CAEC,GACD,WAIE"}
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@jakubmazanec/carson",
3
- "version": "0.1.0-2d1145f",
3
+ "version": "0.1.0-2f12db0",
4
4
  "description": "Tool for managing workspace and project configuration.",
5
5
  "repository": {
6
6
  "type": "git",
7
- "url": "https://github.com/jakubmazanec/js-tools",
7
+ "url": "git@github.com:jakubmazanec/js-tools.git",
8
8
  "directory": "packages/carson"
9
9
  },
10
10
  "license": "AGPL-3.0-only",
@@ -31,13 +31,13 @@
31
31
  "typecheck": "tsc --project tsconfig.typecheck.json"
32
32
  },
33
33
  "dependencies": {
34
- "@jakubmazanec/args": "1.0.1-2d1145f",
35
- "@jakubmazanec/cli": "0.1.0-2d1145f",
36
- "@jakubmazanec/error": "2.0.1-2d1145f",
37
- "@jakubmazanec/fs-utils": "0.1.0-2d1145f",
38
- "@jakubmazanec/template": "1.0.1-2d1145f",
39
- "@jakubmazanec/ts-utils": "2.1.0-2d1145f",
40
- "@jakubmazanec/zod-utils": "1.0.0-2d1145f",
34
+ "@jakubmazanec/args": "1.0.1-2f12db0",
35
+ "@jakubmazanec/cli": "0.1.0-2f12db0",
36
+ "@jakubmazanec/error": "2.0.1-2f12db0",
37
+ "@jakubmazanec/fs-utils": "0.1.0-2f12db0",
38
+ "@jakubmazanec/template": "1.0.1-2f12db0",
39
+ "@jakubmazanec/ts-utils": "2.1.0-2f12db0",
40
+ "@jakubmazanec/zod-utils": "1.0.0-2f12db0",
41
41
  "chalk": "^5.2.0",
42
42
  "diff": "^5.1.0",
43
43
  "fast-glob": "^3.2.11",
@@ -1,7 +1,9 @@
1
1
  import {z} from 'zod';
2
2
 
3
+ // TODO: add strategy 'none' so when you using template A that extends template B, you can overwrite template files to no-op
3
4
  let strategyTemplateAttributeSchema = z.union([
4
5
  z.literal('create'),
6
+ z.literal('ensure'),
5
7
  z.literal('overwrite'),
6
8
  z.literal('merge'),
7
9
  z.literal('check'),
@@ -10,7 +12,6 @@ let strategyTemplateAttributeSchema = z.union([
10
12
 
11
13
  export const templateAttributesSchema = z
12
14
  .object({
13
- // TODO: add strategy 'none' so when you using template A that extends template B, you can overwrite template files to no-op
14
15
  strategy: strategyTemplateAttributeSchema,
15
16
  })
16
17
  .strict();
@@ -1,3 +1,5 @@
1
+ // TODO: can this be fixed?
2
+ /* eslint-disable complexity -- TODO */
1
3
  import fs from 'fs-extra';
2
4
  import path from 'node:path';
3
5
  import prettier from 'prettier';
@@ -34,6 +36,7 @@ export async function applyTemplateRenders({
34
36
  // we need to clear Prettier cache, because we are changing file system
35
37
  prettier.clearConfigCache();
36
38
 
39
+ // first, we read the existing snapshots and use them to undo previous changes
37
40
  let templateRenderSnapshotsPath = path.join(
38
41
  targetPath,
39
42
  CARSON_CONFIG_DIRECTORY,
@@ -53,6 +56,12 @@ export async function applyTemplateRenders({
53
56
  continue;
54
57
  }
55
58
 
59
+ if (templateRenderSnapshot.attributes.strategy === 'ensure') {
60
+ await deleteUsingTemplateRender(templateRenderSnapshot, targetPath, {
61
+ onlyDeleteWhenContentsMatch: true,
62
+ });
63
+ }
64
+
56
65
  if (templateRenderSnapshot.attributes.strategy === 'overwrite') {
57
66
  await deleteUsingTemplateRender(templateRenderSnapshot, targetPath);
58
67
  }
@@ -67,14 +76,32 @@ export async function applyTemplateRenders({
67
76
  }
68
77
  }
69
78
 
70
- if (!ignoreStrategies.includes('create')) {
71
- for (let templateRender of templateRenders) {
72
- if (templateRender.attributes.strategy === 'create') {
79
+ // second, we apply strategies that create files
80
+ for (let templateRender of templateRenders) {
81
+ let strategy =
82
+ typeof templateRender.attributes.strategy === 'string'
83
+ ? templateRender.attributes.strategy
84
+ : templateRender.attributes.strategy[0];
85
+
86
+ if (ignoreStrategies.includes(strategy)) {
87
+ continue;
88
+ }
89
+
90
+ if (templateRender.attributes.strategy === 'create') {
91
+ await createUsingTemplateRender(templateRender, targetPath);
92
+ }
93
+
94
+ if (templateRender.attributes.strategy === 'ensure') {
95
+ let to = path.join(targetPath, templateRender.attributes.to);
96
+ let doesToExist = await fs.pathExists(to);
97
+
98
+ if (!doesToExist) {
73
99
  await createUsingTemplateRender(templateRender, targetPath);
74
100
  }
75
101
  }
76
102
  }
77
103
 
104
+ // third, we apply strategies that change files
78
105
  for (let templateRender of templateRenders) {
79
106
  let strategy =
80
107
  typeof templateRender.attributes.strategy === 'string'
@@ -1,16 +1,35 @@
1
1
  import fs from 'fs-extra';
2
2
  import path from 'node:path';
3
3
 
4
+ import {prettify} from './prettify.js';
4
5
  import {type TemplateRenderSnapshot} from './TemplateRenderSnapshot.js';
5
6
  import {type TemplateRender} from '../TemplateRender.js';
6
7
 
8
+ export type DeleteUsingTemplateRenderOptions = {
9
+ onlyDeleteWhenContentsMatch?: boolean;
10
+ };
11
+
7
12
  export async function deleteUsingTemplateRender(
8
13
  templateRender: TemplateRender | TemplateRenderSnapshot,
9
14
  basePath: string,
15
+ options?: DeleteUsingTemplateRenderOptions,
10
16
  ) {
11
17
  let to = path.join(basePath, templateRender.attributes.to);
12
18
 
13
- if (await fs.pathExists(to)) {
14
- await fs.rm(to);
19
+ if (!(await fs.pathExists(to))) {
20
+ return;
21
+ }
22
+
23
+ if (options?.onlyDeleteWhenContentsMatch) {
24
+ let existingContent = await fs.readFile(to, {encoding: 'utf8'});
25
+ let newContent = await prettify(templateRender.content, to);
26
+
27
+ if (existingContent.trim() === newContent.trim()) {
28
+ await fs.rm(to);
29
+ }
30
+
31
+ return;
15
32
  }
33
+
34
+ await fs.rm(to);
16
35
  }
@@ -12,6 +12,7 @@ export async function saveTemplateRenderSnapshots(
12
12
 
13
13
  for (let templateRender of templateRenders) {
14
14
  if (
15
+ templateRender.attributes.strategy === 'ensure' ||
15
16
  templateRender.attributes.strategy === 'overwrite' ||
16
17
  templateRender.attributes.strategy === 'merge' ||
17
18
  templateRender.attributes.strategy[0] === 'insert'
@@ -1,6 +1,13 @@
1
+ import {pathToPosixPath} from '@jakubmazanec/fs-utils';
1
2
  import _ from 'lodash';
3
+ import path from 'node:path';
2
4
 
3
- import {importCarsonTemplate, readCarsonTemplate} from './internals.js';
5
+ import {
6
+ importCarsonTemplate,
7
+ parseCarsonTemplateId,
8
+ readCarsonTemplate,
9
+ resolveModule,
10
+ } from './internals.js';
4
11
  import {type TemplateData} from './TemplateData.js';
5
12
  import {type TemplateRenders} from './TemplateRenders.js';
6
13
 
@@ -15,6 +22,8 @@ export type RenderCarsonTemplateOptions = {
15
22
  export async function renderCarsonTemplate(
16
23
  options: RenderCarsonTemplateOptions,
17
24
  ): Promise<TemplateRenders> {
25
+ let [moduleId] = parseCarsonTemplateId(options.templateId);
26
+ let modulePath = await resolveModule(moduleId);
18
27
  let template = await readCarsonTemplate(options.templateId);
19
28
  let templateModule = await importCarsonTemplate(options.templateId);
20
29
  let templateRenders: TemplateRenders = [];
@@ -29,7 +38,14 @@ export async function renderCarsonTemplate(
29
38
  },
30
39
  {disablePrettier: true},
31
40
  )
32
- ).map((templateRender) => ({...templateRender, template: {path: file.path ?? ''}})),
41
+ ).map((templateRender) => ({
42
+ ...templateRender,
43
+ template: {
44
+ path: pathToPosixPath(
45
+ `${moduleId}/${path.relative(modulePath, file.path ?? modulePath)}`,
46
+ ),
47
+ },
48
+ })),
33
49
  );
34
50
  }
35
51
 
@@ -1,5 +1,5 @@
1
1
  /**
2
- * {@link Workspace.create | Workspace.create} options parameter.
2
+ * {@link Workspace.create} options parameter.
3
3
  */
4
4
  export type WorkspaceCreateOptions = {
5
5
  path: string;