@synopackageland/templates 0.41.5 → 0.42.0

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 (29) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/README.md +9 -7
  3. package/package.json +5 -1
  4. package/templates/_shared/AGENTS.md +11 -11
  5. package/templates/_shared/package.json +3 -2
  6. package/templates/_shared/pnpm-lock.yaml +1636 -0
  7. package/templates/_shared/pnpm-workspace.yaml +2 -0
  8. package/templates/_shared/third-party-notices.mjs +75 -9
  9. package/templates/binary/README.md +3 -3
  10. package/templates/compose/README.md +3 -3
  11. package/templates/native-go/README.md +6 -6
  12. package/templates/native-go-vue/README.md +10 -10
  13. package/templates/native-go-vue/frontend/DESIGN.md +3 -3
  14. package/templates/native-go-vue/frontend/package.json +4 -2
  15. package/templates/native-go-vue/frontend/pnpm-lock.yaml +2269 -0
  16. package/templates/native-go-vue/frontend/pnpm-workspace.yaml +2 -0
  17. package/templates/native-node/README.md +8 -8
  18. package/templates/native-node/backend/app/package.json +2 -1
  19. package/templates/native-node/backend/app/pnpm-workspace.yaml +2 -0
  20. package/templates/native-node/backend/package.json +2 -1
  21. package/templates/native-node/backend/pnpm-lock.yaml +207 -0
  22. package/templates/native-node/dsm-package.yaml +1 -1
  23. package/templates/native-node-vue/README.md +9 -9
  24. package/templates/native-python/README.md +4 -4
  25. package/templates/native-python-vue/README.md +9 -9
  26. package/templates/native-rust/README.md +4 -4
  27. package/templates/native-rust-vue/README.md +9 -9
  28. package/templates/native-go-vue/frontend/package-lock.json +0 -3115
  29. package/templates/native-node/backend/package-lock.json +0 -59
@@ -0,0 +1,2 @@
1
+ allowBuilds:
2
+ esbuild: true
@@ -1,24 +1,90 @@
1
- import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, writeFileSync } from 'node:fs';
2
- import { join } from 'node:path';
1
+ import { existsSync, mkdirSync, readFileSync, readdirSync, realpathSync, rmSync, writeFileSync } from 'node:fs';
2
+ import { join, resolve } from 'node:path';
3
+ import { createRequire } from 'node:module';
4
+ import { parseAllDocuments } from 'yaml';
3
5
 
4
6
  const OWNED = ['THIRD_PARTY_NOTICES.txt', 'inventory.json', 'UI-LICENSE', 'UI-THIRD_PARTY.md'];
5
7
 
8
+ function productionPackages(root) {
9
+ if (!existsSync(join(root, 'pnpm-lock.yaml'))) {
10
+ const lock = JSON.parse(readFileSync(join(root, 'package-lock.json'), 'utf8'));
11
+ if (lock.lockfileVersion < 2 || !lock.packages) throw new Error('Notice generation requires a v2/v3 npm lockfile');
12
+ return Object.entries(lock.packages).filter(([path, entry]) => path && !entry.dev)
13
+ .map(([path, entry]) => [path, entry, join(root, path)]);
14
+ }
15
+ const documents = parseAllDocuments(readFileSync(join(root, 'pnpm-lock.yaml'), 'utf8'));
16
+ if (documents.some(document => document.errors.length)) throw new Error('Invalid pnpm notice lockfile');
17
+ const lock = documents.at(-1)?.toJSON();
18
+ if (String(lock.lockfileVersion) !== '9.0' || !lock.importers?.['.'] || !lock.snapshots)
19
+ throw new Error('Notice generation requires a pnpm lockfile with importers and snapshots');
20
+ const importer = lock.importers['.'];
21
+ const manifest = JSON.parse(readFileSync(join(root, 'package.json'), 'utf8'));
22
+ // Keep the inventory's logical paths stable across package-manager layouts.
23
+ // Conflicting transitive versions remain beneath their owning dependency.
24
+ const versions = new Map();
25
+ for (const key of Object.keys(lock.packages ?? {})) {
26
+ const match = /^((?:@[^/]+\/)?[^@]+)@(.+)$/.exec(key);
27
+ if (!match) continue;
28
+ const [, name, version] = match;
29
+ const set = versions.get(name) ?? new Set();
30
+ set.add(version);
31
+ versions.set(name, set);
32
+ }
33
+ for (const group of ['dependencies', 'optionalDependencies'])
34
+ for (const [name, spec] of Object.entries(manifest[group] ?? {}))
35
+ if (importer[group]?.[name]?.specifier !== spec) throw new Error('Notice lockfile is out of date; run pnpm install');
36
+ const queue = [];
37
+ const enqueue = (entries, parent, path, optional = false) => {
38
+ for (const [name, ref] of Object.entries(entries ?? {}))
39
+ queue.push({name, ref: typeof ref === 'string' ? ref : ref.version, parent, path, optional});
40
+ };
41
+ enqueue(importer.dependencies, root, '');
42
+ enqueue(importer.optionalDependencies, root, '', true);
43
+ const seen = new Set(), paths = new Set(), entries = [];
44
+ while (queue.length) {
45
+ const {name, ref, parent, path: parentPath, optional} = queue.shift();
46
+ const key = `${name}@${ref}`;
47
+ const direct = importer.dependencies?.[name] ?? importer.optionalDependencies?.[name];
48
+ const nested = parentPath && versions.get(name)?.size > 1 && direct?.version !== ref;
49
+ const seenKey = nested ? `${key} from ${parent}` : key;
50
+ if (seen.has(seenKey)) continue;
51
+ seen.add(seenKey);
52
+ const snapshot = lock.snapshots[key];
53
+ const packageKey = key.replace(/\(.*$/, '');
54
+ const entry = lock.packages?.[packageKey];
55
+ if (!snapshot || !entry) throw new Error(`Missing locked notice dependency: ${key}`);
56
+ const search = createRequire(join(parent, 'package.json')).resolve.paths(name) ?? [];
57
+ const installed = search.map(dir => join(dir, name)).find(dir => existsSync(join(dir, 'package.json')));
58
+ if (!installed && optional) continue;
59
+ if (!installed) throw new Error(`Missing installed notice dependency: ${name}; run pnpm install --frozen-lockfile`);
60
+ const dir = realpathSync(installed);
61
+ let path = nested ? `${parentPath}/node_modules/${name}` : `node_modules/${name}`;
62
+ if (paths.has(path)) path = `${parentPath}/node_modules/${name}`;
63
+ paths.add(path);
64
+ // A local tarball ref names a file; pnpm records its version separately.
65
+ const version = entry.resolution?.type === 'directory'
66
+ ? JSON.parse(readFileSync(join(dir, 'package.json'), 'utf8')).version
67
+ : entry.version ?? ref.replace(/\(.*$/, '');
68
+ entries.push([path, {version, integrity: entry.resolution?.integrity}, dir]);
69
+ enqueue(snapshot.dependencies, dir, path);
70
+ enqueue(snapshot.optionalDependencies, dir, path, true);
71
+ }
72
+ return entries;
73
+ }
74
+
6
75
  // Conservative inventory of every locked production dependency, including
7
76
  // dependencies tree-shaken from the bundle. Build tools marked dev are omitted.
8
77
  // Read actual upstream license/notice files, never synthesize license text from
9
- // SPDX metadata. npm ci must have installed the exact lock before this runs.
78
+ // SPDX metadata. Install the exact lock before this runs.
10
79
  export function writeThirdPartyNotices(root, output, { ui = false } = {}) {
11
- const lock = JSON.parse(readFileSync(join(root, 'package-lock.json'), 'utf8'));
12
- if (lock.lockfileVersion < 2 || !lock.packages) throw new Error('Notice generation requires a v2/v3 npm lockfile');
80
+ root = resolve(root);
13
81
  const inventory = [];
14
82
  const sections = [];
15
83
  let uiFiles;
16
- for (const [path, entry] of Object.entries(lock.packages).sort(([a], [b]) => a.localeCompare(b, 'en'))) {
17
- if (!path || entry.dev) continue;
84
+ for (const [path, entry, dir] of productionPackages(root).sort(([a], [b]) => a.localeCompare(b, 'en'))) {
18
85
  if (!/^node_modules\//.test(path) || path.split('/').includes('..') || entry.link) throw new Error(`Unsupported notice package path: ${path}`);
19
- const dir = join(root, path);
20
86
  const pkg = JSON.parse(readFileSync(join(dir, 'package.json'), 'utf8'));
21
- if (pkg.version !== entry.version) throw new Error(`Notice version mismatch: ${path}; run npm ci`);
87
+ if (pkg.version !== entry.version) throw new Error(`Notice version mismatch: ${path}; run pnpm install --frozen-lockfile`);
22
88
  const files = readdirSync(dir, { withFileTypes: true })
23
89
  .filter(f => f.isFile() && /^(licen[sc]e|copying|notice|copyright)([.-]|$)/i.test(f.name))
24
90
  .map(f => f.name).sort();
@@ -7,12 +7,12 @@ Copy `.env.example` to the ignored `.env` only for optional AI tests; keep real
7
7
  From the project root, install the test tools before running the optional tests:
8
8
 
9
9
  ```sh
10
- npm install --include=dev
11
- npm test
10
+ pnpm install --prod=false
11
+ pnpm test
12
12
  ```
13
13
 
14
14
  `NODE_ENV=production` skips devDependencies; `test:local` needs the CLI.
15
- After committing the generated root lockfile, use `npm ci --include=dev`.
15
+ After committing the generated root lockfile, use `pnpm install --frozen-lockfile --prod=false`.
16
16
  The scaffold provides example prebuilt binaries; replace them with your own
17
17
  Linux binaries before packaging. See `AGENTS.md` for the package workflow.
18
18
 
@@ -7,12 +7,12 @@ Copy `.env.example` to the ignored `.env` only for optional AI tests; keep real
7
7
  From the project root, install the test tools before running the optional tests:
8
8
 
9
9
  ```sh
10
- npm install --include=dev
11
- npm test
10
+ pnpm install --prod=false
11
+ pnpm test
12
12
  ```
13
13
 
14
14
  `NODE_ENV=production` skips devDependencies; `test:local` needs the CLI.
15
- After committing the generated root lockfile, use `npm ci --include=dev`.
15
+ After committing the generated root lockfile, use `pnpm install --frozen-lockfile --prod=false`.
16
16
  Configure the upstream image and its real readiness endpoint before
17
17
  `test:local`; see `AGENTS.md` for the Compose packaging workflow.
18
18
 
@@ -36,13 +36,13 @@ Copy `.env.example` to the ignored `.env` only for optional AI tests; keep real
36
36
  From the project root, install the test tools before running the optional tests:
37
37
 
38
38
  ```sh
39
- npm install --include=dev
40
- npm test
39
+ pnpm install --prod=false
40
+ pnpm test
41
41
  ```
42
42
 
43
43
  `NODE_ENV=production` skips devDependencies; `test:local` needs the CLI.
44
- After committing the generated root lockfile, use `npm ci --include=dev`.
45
- Before `npm run test:local`, prepare the backend and, for Vue, build the
44
+ After committing the generated root lockfile, use `pnpm install --frozen-lockfile --prod=false`.
45
+ Before `pnpm run test:local`, prepare the backend and, for Vue, build the
46
46
  frontend using the steps below and the backend section in `AGENTS.md`, then
47
47
  run `synopkgland ctl generate`.
48
48
 
@@ -282,7 +282,7 @@ ui:
282
282
  dir: web/dist
283
283
  build:
284
284
  cwd: web
285
- command: [npm, run, build]
285
+ command: [pnpm, run, build]
286
286
  ```
287
287
 
288
288
  The build must emit `app.js` (a classic script that registers `mount`) or
@@ -490,7 +490,7 @@ select `ui: false`. For an upstream web panel with no SDK UI, set `ui: false`
490
490
  in `synology/dsm-package.yaml` and configure `identity_gateway` for the panel.
491
491
  Remove `synology/ui/` (or `synology/frontend/` for Vue),
492
492
  `synology/tests/e2e/`, `synology/e2e.config.ts` and
493
- `synology/e2e.ai.config.ts`, and remove the `e2e:*` npm scripts.
493
+ `synology/e2e.ai.config.ts`, and remove the `e2e:*` package scripts.
494
494
  Keep `synology/backend/`, `synology/i18n/messages/`, `synology/operations.json` and
495
495
  the local/ctl tests: the entry gate still needs the backend and message catalogs.
496
496
  Read `synopkgland skill show upstream-fork` and
@@ -14,13 +14,13 @@ Copy `.env.example` to the ignored `.env` only for optional AI tests; keep real
14
14
  From the project root, install the test tools before running the optional tests:
15
15
 
16
16
  ```sh
17
- npm install --include=dev
18
- npm test
17
+ pnpm install --prod=false
18
+ pnpm test
19
19
  ```
20
20
 
21
21
  `NODE_ENV=production` skips devDependencies; `test:local` needs the CLI.
22
- After committing the generated root lockfile, use `npm ci --include=dev`.
23
- Before `npm run test:local`, prepare the backend and, for Vue, build the
22
+ After committing the generated root lockfile, use `pnpm install --frozen-lockfile --prod=false`.
23
+ Before `pnpm run test:local`, prepare the backend and, for Vue, build the
24
24
  frontend using the steps below and the backend section in `AGENTS.md`, then
25
25
  run `synopkgland ctl generate`.
26
26
 
@@ -37,9 +37,9 @@ See the [fork guide](https://github.com/synopackageland/syno-pkg-land-templates#
37
37
 
38
38
  ```sh
39
39
  cd frontend
40
- npm ci --include=dev
41
- npm test
42
- npm run build
40
+ pnpm install --frozen-lockfile --prod=false
41
+ pnpm test
42
+ pnpm run build
43
43
  ```
44
44
 
45
45
  The base app reads package status and edits the package greeting through
@@ -139,7 +139,7 @@ Read `synopkgland skill show ui-vue` for direct Vue component work and
139
139
  `synopkgland skill show ui-json` for optional declarative specs and their
140
140
  validate/render workflow. The existing host/backend lifecycle remains in place.
141
141
 
142
- For UI discovery, run `npx --no syno-pkg-land-ui catalog --json` and
143
- `npx --no syno-pkg-land-ui describe Button --json` from the installed frontend.
142
+ For UI discovery, run `pnpm exec syno-pkg-land-ui catalog --json` and
143
+ `pnpm exec syno-pkg-land-ui describe Button --json` from the installed frontend.
144
144
  Read its prompt and exported Vue docs/types before choosing component APIs.
145
- Use `syno-pkg-land-ui` directly in npm scripts; keep AI guidance version-independent.
145
+ Use `syno-pkg-land-ui` directly in package scripts; keep AI guidance version-independent.
@@ -22,10 +22,10 @@ spacing:
22
22
 
23
23
  Read `AGENTS.md` at the project root and `synopkgland skill show ui-vue` before UI edits.
24
24
 
25
- For UI discovery, run `npx --no syno-pkg-land-ui catalog --json` and
26
- `npx --no syno-pkg-land-ui describe Button --json` from the installed frontend.
25
+ For UI discovery, run `pnpm exec syno-pkg-land-ui catalog --json` and
26
+ `pnpm exec syno-pkg-land-ui describe Button --json` from the installed frontend.
27
27
  Read its prompt and exported Vue docs/types before choosing component APIs.
28
- Use `syno-pkg-land-ui` directly in npm scripts; keep AI guidance version-independent.
28
+ Use `syno-pkg-land-ui` directly in package scripts; keep AI guidance version-independent.
29
29
 
30
30
  ## Overview
31
31
 
@@ -18,6 +18,8 @@
18
18
  "typescript": "5.9.3",
19
19
  "vite": "7.3.6",
20
20
  "vitest": "4.1.11",
21
- "vue-tsc": "3.3.11"
22
- }
21
+ "vue-tsc": "3.3.11",
22
+ "yaml": "^2.9.1"
23
+ },
24
+ "packageManager": "pnpm@12.18.0"
23
25
  }