@apollion-dsi/scripts 0.8.1 → 0.9.1

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 (62) hide show
  1. package/CHANGELOG.md +59 -0
  2. package/coverage/clover.xml +29 -5
  3. package/coverage/coverage-final.json +3 -1
  4. package/coverage/lcov-report/config/env.ts.html +2 -2
  5. package/coverage/lcov-report/config/index.html +1 -1
  6. package/coverage/lcov-report/config/paths.ts.html +1 -1
  7. package/coverage/lcov-report/config/shared.ts.html +1 -1
  8. package/coverage/lcov-report/index.html +18 -18
  9. package/coverage/lcov-report/utils/buildWrapperArgs.ts.html +196 -0
  10. package/coverage/lcov-report/utils/checkRequiredFiles.ts.html +1 -1
  11. package/coverage/lcov-report/utils/formatWebpackMessages.ts.html +1 -1
  12. package/coverage/lcov-report/utils/getPublicUrlOrPath.ts.html +1 -1
  13. package/coverage/lcov-report/utils/index.html +38 -8
  14. package/coverage/lcov-report/utils/resolveBin.ts.html +193 -0
  15. package/coverage/lcov.info +54 -2
  16. package/eslint.config.js +4 -4
  17. package/lib/bin.js +8 -3
  18. package/lib/bin.js.map +1 -1
  19. package/lib/command/audit.d.ts +1 -0
  20. package/lib/command/audit.js +44 -0
  21. package/lib/command/audit.js.map +1 -0
  22. package/lib/command/check-types.d.ts +1 -0
  23. package/lib/command/check-types.js +18 -0
  24. package/lib/command/check-types.js.map +1 -0
  25. package/lib/command/create/helper.js +0 -1
  26. package/lib/command/create/helper.js.map +1 -1
  27. package/lib/command/create/index.js +7 -5
  28. package/lib/command/create/index.js.map +1 -1
  29. package/lib/command/lint.d.ts +1 -0
  30. package/lib/command/lint.js +27 -0
  31. package/lib/command/lint.js.map +1 -0
  32. package/lib/command/prettier.d.ts +1 -0
  33. package/lib/command/prettier.js +26 -0
  34. package/lib/command/prettier.js.map +1 -0
  35. package/lib/command/validate.d.ts +1 -0
  36. package/lib/command/validate.js +51 -0
  37. package/lib/command/validate.js.map +1 -0
  38. package/lib/utils/buildWrapperArgs.d.ts +32 -0
  39. package/lib/utils/buildWrapperArgs.js +39 -0
  40. package/lib/utils/buildWrapperArgs.js.map +1 -0
  41. package/lib/utils/resolveBin.d.ts +13 -0
  42. package/lib/utils/resolveBin.js +35 -0
  43. package/lib/utils/resolveBin.js.map +1 -0
  44. package/llms.txt +56 -2
  45. package/package.json +9 -4
  46. package/src/__tests__/buildWrapperArgs.test.ts +73 -0
  47. package/src/__tests__/resolveBin.test.ts +68 -0
  48. package/src/bin.ts +11 -3
  49. package/src/command/audit.ts +46 -0
  50. package/src/command/check-types.ts +18 -0
  51. package/src/command/create/helper.ts +0 -1
  52. package/src/command/create/index.ts +7 -5
  53. package/src/command/lint.ts +27 -0
  54. package/src/command/prettier.ts +27 -0
  55. package/src/command/validate.ts +66 -0
  56. package/src/utils/buildWrapperArgs.ts +37 -0
  57. package/src/utils/resolveBin.ts +36 -0
  58. package/template/src/App.tsx +5 -2
  59. package/template/src/index.tsx +8 -6
  60. package/template/tsconfig.json +3 -24
  61. package/tsconfig.base.json +19 -0
  62. package/template/audit-ci.json +0 -5
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prettier.js","sourceRoot":"","sources":["../../src/command/prettier.ts"],"names":[],"mappings":";;;;;AAAA,8DAAgC;AAEhC,gEAA6D;AAC7D,oDAAiD;AAEjD;;;;;;;GAOG;AACH,MAAM,WAAW,GAAG,IAAA,uBAAU,EAAC,UAAU,CAAC,CAAC;AAE3C,MAAM,YAAY,GAAG,+BAA+B,CAAC;AAErD,MAAM,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AAEvC,MAAM,IAAI,GAAG,IAAA,mCAAgB,EAAC,QAAQ,EAAE;IACtC,QAAQ,EAAE,CAAC,SAAS,EAAE,YAAY,CAAC;IACnC,MAAM,EAAE,YAAY;CACrB,CAAC,CAAC;AAEH,MAAM,MAAM,GAAG,qBAAK,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,WAAW,EAAE,GAAG,IAAI,CAAC,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC;AAE1F,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,IAAI,CAAC,CAAC,CAAC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const chalk_1 = __importDefault(require("chalk"));
7
+ const cross_spawn_1 = __importDefault(require("cross-spawn"));
8
+ /**
9
+ * `scripts validate` — Apollion's standard pre-merge gate. Runs
10
+ * `check-types`, `prettier --check`, `lint` (parallel) then `test`
11
+ * with `--watchAll=false` (sequential). Bails on the first failure.
12
+ *
13
+ * Mirrors the Apollion ecosystem's own `scripts/validate.sh`
14
+ * orchestration so consumers don't have to reinvent it.
15
+ */
16
+ const userArgs = process.argv.slice(2);
17
+ const skip = new Set(userArgs.filter((arg) => arg.startsWith('--skip=')).flatMap((arg) => arg.slice('--skip='.length).split(',')));
18
+ const parallel = [
19
+ { name: 'check-types', cmd: 'check-types' },
20
+ { name: 'prettier', cmd: 'prettier' },
21
+ { name: 'lint', cmd: 'lint' },
22
+ ].filter((s) => !skip.has(s.name));
23
+ const sequential = [{ name: 'test', cmd: 'test --watchAll=false' }].filter((s) => !skip.has(s.name));
24
+ function runStep(step) {
25
+ return new Promise((resolve) => {
26
+ console.log(chalk_1.default.cyan(`▶ ${step.name}`));
27
+ const [bin, ...rest] = step.cmd.split(' ');
28
+ const child = (0, cross_spawn_1.default)(process.execPath, [require.resolve(`./${bin}`), ...rest], {
29
+ stdio: 'inherit',
30
+ });
31
+ child.on('close', (code) => resolve(code ?? 1));
32
+ });
33
+ }
34
+ (async () => {
35
+ const parallelResults = await Promise.all(parallel.map(runStep));
36
+ const parallelFailures = parallel.filter((_, i) => parallelResults[i] !== 0);
37
+ if (parallelFailures.length > 0) {
38
+ console.error(chalk_1.default.red(`✘ validate failed: ${parallelFailures.map((s) => s.name).join(', ')}`));
39
+ process.exit(1);
40
+ }
41
+ for (const step of sequential) {
42
+ const code = await runStep(step);
43
+ if (code !== 0) {
44
+ console.error(chalk_1.default.red(`✘ validate failed: ${step.name}`));
45
+ process.exit(code);
46
+ }
47
+ }
48
+ console.log(chalk_1.default.green('✓ validate passed'));
49
+ process.exit(0);
50
+ })();
51
+ //# sourceMappingURL=validate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate.js","sourceRoot":"","sources":["../../src/command/validate.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Build the argv passed to an underlying CLI (eslint, prettier) for a
3
+ * `scripts <cmd>` wrapper, from the user-provided argv.
4
+ *
5
+ * - Empty input → `defaults` verbatim (the canonical no-args
6
+ * invocation, e.g. `['src', '--quiet']` for lint).
7
+ * - Non-empty input → user args (with the POSIX `--` end-of-options
8
+ * marker removed) followed by `target`.
9
+ *
10
+ * The previous heuristic tried to detect "user-provided path" by
11
+ * scanning for any argv entry not starting with `-`, then handed full
12
+ * control to the user. That broke two real consumer invocations:
13
+ *
14
+ * 1. `scripts lint --max-warnings 0` — the `0` value of the flag was
15
+ * misread as a path, so `src` was dropped and ESLint ran with no
16
+ * files.
17
+ * 2. `scripts lint -- --fix` — every arg started with `-`, so `src`
18
+ * was appended, but the surviving `--` separator forced ESLint to
19
+ * treat `--fix` and `src` as positional files.
20
+ *
21
+ * The wrapper is intentionally narrow: pass flags, get the default
22
+ * target. Consumers that need a non-default target should invoke the
23
+ * underlying CLI directly (`node_modules/.bin/eslint <path>`).
24
+ *
25
+ * @param userArgs - argv after `scripts <cmd>` (i.e. `process.argv.slice(2)`).
26
+ * @param options.defaults - argv to use when `userArgs` is empty.
27
+ * @param options.target - path/glob appended after user flags otherwise.
28
+ */
29
+ export declare function buildWrapperArgs(userArgs: string[], options: {
30
+ defaults: string[];
31
+ target: string;
32
+ }): string[];
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildWrapperArgs = buildWrapperArgs;
4
+ /**
5
+ * Build the argv passed to an underlying CLI (eslint, prettier) for a
6
+ * `scripts <cmd>` wrapper, from the user-provided argv.
7
+ *
8
+ * - Empty input → `defaults` verbatim (the canonical no-args
9
+ * invocation, e.g. `['src', '--quiet']` for lint).
10
+ * - Non-empty input → user args (with the POSIX `--` end-of-options
11
+ * marker removed) followed by `target`.
12
+ *
13
+ * The previous heuristic tried to detect "user-provided path" by
14
+ * scanning for any argv entry not starting with `-`, then handed full
15
+ * control to the user. That broke two real consumer invocations:
16
+ *
17
+ * 1. `scripts lint --max-warnings 0` — the `0` value of the flag was
18
+ * misread as a path, so `src` was dropped and ESLint ran with no
19
+ * files.
20
+ * 2. `scripts lint -- --fix` — every arg started with `-`, so `src`
21
+ * was appended, but the surviving `--` separator forced ESLint to
22
+ * treat `--fix` and `src` as positional files.
23
+ *
24
+ * The wrapper is intentionally narrow: pass flags, get the default
25
+ * target. Consumers that need a non-default target should invoke the
26
+ * underlying CLI directly (`node_modules/.bin/eslint <path>`).
27
+ *
28
+ * @param userArgs - argv after `scripts <cmd>` (i.e. `process.argv.slice(2)`).
29
+ * @param options.defaults - argv to use when `userArgs` is empty.
30
+ * @param options.target - path/glob appended after user flags otherwise.
31
+ */
32
+ function buildWrapperArgs(userArgs, options) {
33
+ if (userArgs.length === 0) {
34
+ return [...options.defaults];
35
+ }
36
+ const passthrough = userArgs.filter((a) => a !== '--');
37
+ return [...passthrough, options.target];
38
+ }
39
+ //# sourceMappingURL=buildWrapperArgs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"buildWrapperArgs.js","sourceRoot":"","sources":["../../src/utils/buildWrapperArgs.ts"],"names":[],"mappings":";;AA4BA,4CAQC;AApCD;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,SAAgB,gBAAgB,CAAC,QAAkB,EAAE,OAA+C;IAClG,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1B,OAAO,CAAC,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;IAC/B,CAAC;IAED,MAAM,WAAW,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC;IAEvD,OAAO,CAAC,GAAG,WAAW,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;AAC1C,CAAC"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Resolve the absolute path to a dependency's CLI entrypoint as declared
3
+ * in its `package.json#bin`. Works for packages whose `exports` map
4
+ * blocks `require.resolve('<pkg>/bin/...')` (e.g. eslint 9+).
5
+ *
6
+ * @param pkgName - dependency name (e.g. `'eslint'`, `'prettier'`)
7
+ * @param binName - bin name when `package.json#bin` is an object map.
8
+ * Defaults to `pkgName`.
9
+ * @param fromPath - directory to resolve `pkgName` from. Defaults to
10
+ * this file's directory so production calls walk up from
11
+ * `@apollion-dsi/scripts`'s install location.
12
+ */
13
+ export declare function resolveBin(pkgName: string, binName?: string, fromPath?: string): string;
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.resolveBin = resolveBin;
7
+ const module_1 = require("module");
8
+ const path_1 = __importDefault(require("path"));
9
+ /**
10
+ * Resolve the absolute path to a dependency's CLI entrypoint as declared
11
+ * in its `package.json#bin`. Works for packages whose `exports` map
12
+ * blocks `require.resolve('<pkg>/bin/...')` (e.g. eslint 9+).
13
+ *
14
+ * @param pkgName - dependency name (e.g. `'eslint'`, `'prettier'`)
15
+ * @param binName - bin name when `package.json#bin` is an object map.
16
+ * Defaults to `pkgName`.
17
+ * @param fromPath - directory to resolve `pkgName` from. Defaults to
18
+ * this file's directory so production calls walk up from
19
+ * `@apollion-dsi/scripts`'s install location.
20
+ */
21
+ function resolveBin(pkgName, binName = pkgName, fromPath = __dirname) {
22
+ const req = (0, module_1.createRequire)(path_1.default.join(fromPath, 'noop.js'));
23
+ const pkgJsonPath = req.resolve(`${pkgName}/package.json`);
24
+ const pkgJson = req(pkgJsonPath);
25
+ const binField = pkgJson.bin;
26
+ if (!binField) {
27
+ throw new Error(`Package "${pkgName}" has no "bin" field in package.json`);
28
+ }
29
+ const relBin = typeof binField === 'string' ? binField : binField[binName];
30
+ if (!relBin) {
31
+ throw new Error(`Package "${pkgName}" has no bin named "${binName}"`);
32
+ }
33
+ return path_1.default.resolve(path_1.default.dirname(pkgJsonPath), relBin);
34
+ }
35
+ //# sourceMappingURL=resolveBin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolveBin.js","sourceRoot":"","sources":["../../src/utils/resolveBin.ts"],"names":[],"mappings":";;;;;AAeA,gCAoBC;AAnCD,mCAAuC;AACvC,gDAAwB;AAExB;;;;;;;;;;;GAWG;AACH,SAAgB,UAAU,CAAC,OAAe,EAAE,UAAkB,OAAO,EAAE,WAAmB,SAAS;IACjG,MAAM,GAAG,GAAG,IAAA,sBAAa,EAAC,cAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC,CAAC;IAE1D,MAAM,WAAW,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,OAAO,eAAe,CAAC,CAAC;IAE3D,MAAM,OAAO,GAAG,GAAG,CAAC,WAAW,CAA8C,CAAC;IAE9E,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC;IAE7B,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,MAAM,IAAI,KAAK,CAAC,YAAY,OAAO,sCAAsC,CAAC,CAAC;IAC7E,CAAC;IAED,MAAM,MAAM,GAAG,OAAO,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;IAE3E,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CAAC,YAAY,OAAO,uBAAuB,OAAO,GAAG,CAAC,CAAC;IACxE,CAAC;IAED,OAAO,cAAI,CAAC,OAAO,CAAC,cAAI,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC,CAAC;AACzD,CAAC"}
package/llms.txt CHANGED
@@ -15,11 +15,36 @@ behind four commands: `create`, `dev`, `build`, `test`.
15
15
  | `dev` | webpack-dev-server + HMR (react-refresh) at :3000 |
16
16
  | `build` | Production bundle to `build/` |
17
17
  | `test` | Jest 30 in watch mode against changed files |
18
+ | `lint` | ESLint over `src` w/ Apollion config (alias for `eslint src --ext ts,tsx --quiet`) |
19
+ | `prettier` | Prettier `--check` over `src/**/*.{ts,tsx,json,css,md}` |
20
+ | `check-types` (alias `tsc`) | `tsc --noEmit` against the consumer's tsconfig |
21
+ | `audit` | `yarn npm audit --severity moderate --no-deprecations` (falls back to `npm audit --audit-level=moderate`) |
22
+ | `validate` | Pre-merge gate: `check-types` + `prettier` + `lint` in parallel, then `test --watchAll=false`. `--skip=lint,prettier` to drop steps. |
18
23
 
19
- Use via `package.json#scripts`:
24
+ Every command accepts pass-through args — `scripts lint --fix`,
25
+ `scripts prettier --write`, `scripts check-types -p tsconfig.build.json`, etc.
26
+ Do **not** use the POSIX `--` end-of-options separator: `lint` and
27
+ `prettier` always append the default target (`src` / the default glob),
28
+ so `eslint -- --fix src` would treat `--fix` and `src` as positional
29
+ files. Pass flags directly.
30
+
31
+ Use via `package.json#scripts` — the whole consumer file becomes thin:
20
32
 
21
33
  ```json
22
- { "scripts": { "dev": "scripts dev", "build": "scripts build", "test": "scripts test" } }
34
+ {
35
+ "scripts": {
36
+ "dev": "scripts dev",
37
+ "build": "scripts build",
38
+ "test": "scripts test",
39
+ "lint": "scripts lint",
40
+ "lint:fix": "scripts lint --fix",
41
+ "prettier": "scripts prettier",
42
+ "format": "scripts prettier --write",
43
+ "check-types": "scripts check-types",
44
+ "audit-dependencies": "scripts audit",
45
+ "validate": "scripts validate"
46
+ }
47
+ }
23
48
  ```
24
49
 
25
50
  ## Stack (hidden from consumer)
@@ -28,19 +53,48 @@ Use via `package.json#scripts`:
28
53
  - `babel-plugin-relay` + `babel-plugin-styled-components`
29
54
  - `react-refresh` for HMR
30
55
  - Jest 30 + ts-jest + `@testing-library/react`
56
+ - ESLint 9 (`@apollion-dsi/eslint-config`) + Prettier 3
31
57
  - Peer: **TypeScript 6.x**
32
58
 
59
+ Consumers pull *only* `@apollion-dsi/scripts` for tooling — eslint, prettier,
60
+ jest, babel et al. arrive transitively. The consumer's `package.json#scripts`
61
+ should only reference `scripts <cmd>`.
62
+
33
63
  ## Invariants
34
64
 
35
65
  * **Yarn only** (consumer template uses Yarn 4 Berry).
36
66
  * **No webpack config exposed.** If you need a config escape hatch,
37
67
  open a ROADMAP entry — the point of this package is to not have one.
38
68
  * **TypeScript 6 strict** in the template — non-negotiable.
69
+ * **Template tsconfig modern, never legacy.** `target: es2020`,
70
+ `moduleResolution: bundler`, `jsx: react-jsx`. No `es5`, no `node10`,
71
+ no classic JSX — both warn under TS 6.0 (`TS5107`) and break in TS 7.
72
+
73
+ ## tsconfig.base.json (consumer-facing)
74
+
75
+ `@apollion-dsi/scripts` exposes a modern TS base config. Consumers
76
+ extend it instead of copying values:
77
+
78
+ ```jsonc
79
+ // tsconfig.json
80
+ {
81
+ "extends": "@apollion-dsi/scripts/tsconfig.base.json",
82
+ "include": ["src"]
83
+ }
84
+ ```
85
+
86
+ The base sets `target: es2020`, `module: esnext`,
87
+ `moduleResolution: bundler`, `jsx: react-jsx` (automatic runtime — no
88
+ `import React` needed for JSX), `strict`, `isolatedModules`,
89
+ `noEmit`. Overriding any field is fine; the goal is a defaultful SSOT,
90
+ not a lock-in.
39
91
 
40
92
  ## Files
41
93
 
42
94
  - `lib/bin.js` — CLI entry (resolved by `package.json#bin`).
43
95
  - `lib/index.js` — programmatic entry.
96
+ - `tsconfig.base.json` — exported via `package.json#exports` for
97
+ consumer `extends`.
44
98
  - `src/` — TS sources.
45
99
  - `scripts/validate.sh` — workspace `validate` step.
46
100
 
package/package.json CHANGED
@@ -1,9 +1,14 @@
1
1
  {
2
2
  "name": "@apollion-dsi/scripts",
3
- "version": "0.8.1",
3
+ "version": "0.9.1",
4
4
  "description": "Apollion Framework CLI",
5
5
  "main": "lib/index.js",
6
6
  "bin": "lib/bin.js",
7
+ "exports": {
8
+ ".": "./lib/index.js",
9
+ "./tsconfig.base.json": "./tsconfig.base.json",
10
+ "./package.json": "./package.json"
11
+ },
7
12
  "scripts": {
8
13
  "prepare": "yarn build",
9
14
  "audit-dependencies": "yarn npm audit --severity moderate --no-deprecations",
@@ -19,7 +24,7 @@
19
24
  "coverage": "jest --coverage",
20
25
  "validate:tests": "jest --coverage",
21
26
  "build": "tsc -p .",
22
- "release": "yarn build && changeset publish"
27
+ "release": "yarn build && yarn changeset publish"
23
28
  },
24
29
  "keywords": [],
25
30
  "author": "Apollion DS Team",
@@ -47,6 +52,7 @@
47
52
  "detect-port": "2.1.0",
48
53
  "dotenv": "17.4.2",
49
54
  "dotenv-expand": "13.0.0",
55
+ "eslint": "9.29.0",
50
56
  "fork-ts-checker-webpack-plugin": "9.1.0",
51
57
  "fs-extra": "11.3.5",
52
58
  "git-revision-webpack-plugin": "5.0.0",
@@ -56,6 +62,7 @@
56
62
  "jest": "30.4.2",
57
63
  "jest-cli": "30.4.2",
58
64
  "jest-environment-node": "^30.4.1",
65
+ "prettier": "3.8.3",
59
66
  "react-refresh": "0.18.0",
60
67
  "resolve": "1.22.12",
61
68
  "semver": "7.8.0",
@@ -74,9 +81,7 @@
74
81
  "devDependencies": {
75
82
  "@testing-library/dom": "10.4.1",
76
83
  "@types/jest": "30.0.0",
77
- "eslint": "9.29.0",
78
84
  "npm-run-all": "4.1.5",
79
- "prettier": "3.8.3",
80
85
  "react": "19.2.6",
81
86
  "react-dom": "19.2.6",
82
87
  "styled-components": "6.4.1",
@@ -0,0 +1,73 @@
1
+ import { buildWrapperArgs } from '../utils/buildWrapperArgs';
2
+
3
+ describe('buildWrapperArgs', () => {
4
+ const lintOptions = { defaults: ['src', '--quiet'], target: 'src' };
5
+ const prettierOptions = {
6
+ defaults: ['--check', 'src/**/*.{ts,tsx,json,css,md}'],
7
+ target: 'src/**/*.{ts,tsx,json,css,md}',
8
+ };
9
+
10
+ describe('lint wrapper semantics', () => {
11
+ it('falls back to defaults when no user args', () => {
12
+ expect(buildWrapperArgs([], lintOptions)).toEqual(['src', '--quiet']);
13
+ });
14
+
15
+ it('returns a fresh defaults copy (does not alias the input array)', () => {
16
+ const defaults = ['src', '--quiet'];
17
+ const result = buildWrapperArgs([], { defaults, target: 'src' });
18
+
19
+ expect(result).not.toBe(defaults);
20
+ });
21
+
22
+ it('appends target after a single flag (lint --fix)', () => {
23
+ expect(buildWrapperArgs(['--fix'], lintOptions)).toEqual(['--fix', 'src']);
24
+ });
25
+
26
+ it('appends target after --flag=value form', () => {
27
+ expect(buildWrapperArgs(['--max-warnings=0'], lintOptions)).toEqual(['--max-warnings=0', 'src']);
28
+ });
29
+
30
+ it('appends target after --flag value (space-separated) form', () => {
31
+ // Regression: previously the literal `0` was misread as a path,
32
+ // dropping `src` from the eslint invocation so it ran with no files.
33
+ expect(buildWrapperArgs(['--max-warnings', '0'], lintOptions)).toEqual(['--max-warnings', '0', 'src']);
34
+ });
35
+
36
+ it('strips a POSIX `--` end-of-options marker before appending target', () => {
37
+ // Regression: the `--` previously survived into argv, so eslint
38
+ // treated `--fix` and `src` as positional file arguments and failed
39
+ // with "No files matching pattern '--fix'".
40
+ expect(buildWrapperArgs(['--', '--fix'], lintOptions)).toEqual(['--fix', 'src']);
41
+ });
42
+
43
+ it('strips multiple `--` markers (defensive)', () => {
44
+ expect(buildWrapperArgs(['--', '--fix', '--'], lintOptions)).toEqual(['--fix', 'src']);
45
+ });
46
+
47
+ it('preserves the order of user flags', () => {
48
+ expect(buildWrapperArgs(['--cache', '--max-warnings=0', '--fix'], lintOptions)).toEqual([
49
+ '--cache',
50
+ '--max-warnings=0',
51
+ '--fix',
52
+ 'src',
53
+ ]);
54
+ });
55
+ });
56
+
57
+ describe('prettier wrapper semantics', () => {
58
+ it('falls back to --check + DEFAULT_GLOB when no user args', () => {
59
+ expect(buildWrapperArgs([], prettierOptions)).toEqual(['--check', 'src/**/*.{ts,tsx,json,css,md}']);
60
+ });
61
+
62
+ it('appends DEFAULT_GLOB after --write', () => {
63
+ expect(buildWrapperArgs(['--write'], prettierOptions)).toEqual(['--write', 'src/**/*.{ts,tsx,json,css,md}']);
64
+ });
65
+
66
+ it('strips the `--` separator before appending DEFAULT_GLOB', () => {
67
+ expect(buildWrapperArgs(['--', '--write'], prettierOptions)).toEqual([
68
+ '--write',
69
+ 'src/**/*.{ts,tsx,json,css,md}',
70
+ ]);
71
+ });
72
+ });
73
+ });
@@ -0,0 +1,68 @@
1
+ import fs from 'fs';
2
+ import os from 'os';
3
+ import path from 'path';
4
+
5
+ import { resolveBin } from '../utils/resolveBin';
6
+
7
+ describe('resolveBin', () => {
8
+ let tmpRoot: string;
9
+
10
+ beforeEach(() => {
11
+ tmpRoot = fs.realpathSync(fs.mkdtempSync(path.join(os.tmpdir(), 'apollion-scripts-resolveBin-')));
12
+ });
13
+
14
+ afterEach(() => {
15
+ fs.rmSync(tmpRoot, { recursive: true, force: true });
16
+ });
17
+
18
+ function makeFakePkg(name: string, binField: unknown, binFiles: string[] = []): void {
19
+ const pkgDir = path.join(tmpRoot, 'node_modules', name);
20
+ fs.mkdirSync(pkgDir, { recursive: true });
21
+ fs.writeFileSync(path.join(pkgDir, 'package.json'), JSON.stringify({ name, version: '0.0.0', bin: binField }));
22
+
23
+ for (const file of binFiles) {
24
+ const target = path.join(pkgDir, file);
25
+ fs.mkdirSync(path.dirname(target), { recursive: true });
26
+ fs.writeFileSync(target, '#!/usr/bin/env node\n');
27
+ }
28
+ }
29
+
30
+ it('resolves a string-style bin field', () => {
31
+ makeFakePkg('faketool-a', './bin/cli.js', ['bin/cli.js']);
32
+
33
+ const resolved = resolveBin('faketool-a', 'faketool-a', tmpRoot);
34
+
35
+ expect(resolved).toBe(path.join(tmpRoot, 'node_modules', 'faketool-a', 'bin', 'cli.js'));
36
+ });
37
+
38
+ it('resolves a map-style bin field via the pkg name by default', () => {
39
+ makeFakePkg('faketool-b', { 'faketool-b': './bin/main.js', other: './bin/other.js' }, [
40
+ 'bin/main.js',
41
+ 'bin/other.js',
42
+ ]);
43
+
44
+ const resolved = resolveBin('faketool-b', 'faketool-b', tmpRoot);
45
+
46
+ expect(resolved).toBe(path.join(tmpRoot, 'node_modules', 'faketool-b', 'bin', 'main.js'));
47
+ });
48
+
49
+ it('resolves a map-style bin field via an explicit alternative name', () => {
50
+ makeFakePkg('faketool-c', { 'faketool-c': './bin/main.js', alt: './bin/alt.js' }, ['bin/main.js', 'bin/alt.js']);
51
+
52
+ const resolved = resolveBin('faketool-c', 'alt', tmpRoot);
53
+
54
+ expect(resolved).toBe(path.join(tmpRoot, 'node_modules', 'faketool-c', 'bin', 'alt.js'));
55
+ });
56
+
57
+ it('throws if the package has no bin field', () => {
58
+ makeFakePkg('faketool-d', undefined);
59
+
60
+ expect(() => resolveBin('faketool-d', 'faketool-d', tmpRoot)).toThrow(/no "bin" field/);
61
+ });
62
+
63
+ it('throws if the requested bin name is missing from the map', () => {
64
+ makeFakePkg('faketool-e', { 'faketool-e': './bin/main.js' }, ['bin/main.js']);
65
+
66
+ expect(() => resolveBin('faketool-e', 'missing', tmpRoot)).toThrow(/no bin named "missing"/);
67
+ });
68
+ });
package/src/bin.ts CHANGED
@@ -18,10 +18,18 @@ notifier.notify({
18
18
  )} \nRun ${chalk.cyan('yarn add {packageName}')} to update.`,
19
19
  });
20
20
 
21
+ const COMMAND_ALIASES: Record<string, string> = {
22
+ tsc: 'check-types',
23
+ };
24
+
25
+ const SUPPORTED_COMMANDS = new Set(['build', 'dev', 'test', 'lint', 'prettier', 'check-types', 'audit', 'validate']);
26
+
21
27
  (async (cmd: string[]) => {
22
- const [command, ...options] = cmd;
28
+ const [rawCommand, ...options] = cmd;
29
+
30
+ const command = COMMAND_ALIASES[rawCommand] ?? rawCommand;
23
31
 
24
- if (['build', 'dev', 'test'].includes(command)) {
32
+ if (SUPPORTED_COMMANDS.has(command)) {
25
33
  const result = spawn.sync(process.execPath, [require.resolve(`./command/${command}`), ...options], {
26
34
  stdio: 'inherit',
27
35
  });
@@ -35,7 +43,7 @@ notifier.notify({
35
43
  return process.exit(0);
36
44
  }
37
45
 
38
- console.log(`${chalk.red('Commando não reconhecido:')} ${chalk.yellow(command)}`);
46
+ console.log(`${chalk.red('Commando não reconhecido:')} ${chalk.yellow(rawCommand)}`);
39
47
 
40
48
  process.exit(1);
41
49
  })(process.argv.slice(2));
@@ -0,0 +1,46 @@
1
+ import spawn from 'cross-spawn';
2
+
3
+ /**
4
+ * `scripts audit` — delegates to the active package manager's audit
5
+ * command. Defaults match the Apollion ecosystem standard
6
+ * (`severity moderate`, no deprecation noise). User-supplied args are
7
+ * appended.
8
+ *
9
+ * Resolution order:
10
+ * 1. yarn berry (>=2) — `yarn npm audit --severity moderate --no-deprecations`
11
+ * 2. yarn classic (1.x) — `yarn audit --level moderate`
12
+ * 3. npm fallback — `npm audit --audit-level=moderate`
13
+ */
14
+ function probeYarnMajor(): number | null {
15
+ const probe = spawn.sync('yarn', ['--version'], { encoding: 'utf8' });
16
+
17
+ if (probe.status !== 0 || !probe.stdout) {
18
+ return null;
19
+ }
20
+
21
+ const major = Number.parseInt(String(probe.stdout).trim().split('.')[0], 10);
22
+
23
+ return Number.isNaN(major) ? null : major;
24
+ }
25
+
26
+ const userArgs = process.argv.slice(2);
27
+
28
+ const yarnMajor = probeYarnMajor();
29
+
30
+ let command: string;
31
+ let baseArgs: string[];
32
+
33
+ if (yarnMajor !== null && yarnMajor >= 2) {
34
+ command = 'yarn';
35
+ baseArgs = ['npm', 'audit', '--severity', 'moderate', '--no-deprecations'];
36
+ } else if (yarnMajor !== null) {
37
+ command = 'yarn';
38
+ baseArgs = ['audit', '--level', 'moderate'];
39
+ } else {
40
+ command = 'npm';
41
+ baseArgs = ['audit', '--audit-level=moderate'];
42
+ }
43
+
44
+ const result = spawn.sync(command, [...baseArgs, ...userArgs], { stdio: 'inherit' });
45
+
46
+ process.exit(result.status ?? 1);
@@ -0,0 +1,18 @@
1
+ import spawn from 'cross-spawn';
2
+
3
+ import { resolveBin } from '../utils/resolveBin';
4
+
5
+ /**
6
+ * `scripts check-types` (alias `scripts tsc`) — runs `tsc --noEmit`
7
+ * resolving the consumer's TypeScript install (peerDep). Extra args
8
+ * pass through verbatim.
9
+ */
10
+ const tscBin = resolveBin('typescript', 'tsc');
11
+
12
+ const userArgs = process.argv.slice(2);
13
+
14
+ const args = userArgs.length > 0 ? userArgs : ['--noEmit'];
15
+
16
+ const result = spawn.sync(process.execPath, [tscBin, ...args], { stdio: 'inherit' });
17
+
18
+ process.exit(result.status ?? 1);
@@ -21,7 +21,6 @@ export const templateDependencies = [
21
21
  '@apollion-dsi/core',
22
22
  '@apollion-dsi/scripts',
23
23
  '@apollion-dsi/eslint-config',
24
- 'audit-ci',
25
24
  ];
26
25
 
27
26
  export async function projectNameInput() {
@@ -81,11 +81,13 @@ async function createPackageJson(config) {
81
81
  start: 'scripts dev',
82
82
  build: 'scripts build',
83
83
  test: 'scripts test -- --config=jest.config.js',
84
- lint: 'eslint --quiet src --ext ts,tsx',
85
- 'lint:full': 'eslint src --ext ts,tsx',
86
- 'lint:fix': 'eslint --fix src --ext ts,tsx',
87
- 'prettier:write': 'prettier src/ --write',
88
- 'audit-dependencies': 'audit-ci --config audit-ci.json',
84
+ lint: 'scripts lint',
85
+ 'lint:fix': 'scripts lint --fix',
86
+ prettier: 'scripts prettier',
87
+ 'prettier:write': 'scripts prettier --write',
88
+ 'check-types': 'scripts check-types',
89
+ 'audit-dependencies': 'scripts audit',
90
+ validate: 'scripts validate',
89
91
  },
90
92
  husky: {
91
93
  hooks: {
@@ -0,0 +1,27 @@
1
+ import spawn from 'cross-spawn';
2
+
3
+ import { buildWrapperArgs } from '../utils/buildWrapperArgs';
4
+ import { resolveBin } from '../utils/resolveBin';
5
+
6
+ /**
7
+ * `scripts lint` — ESLint over `src`. ESLint 9 flat config uses
8
+ * `eslint.config.js#files` for filtering (no `--ext`).
9
+ *
10
+ * Flag args pass through; the default `src` target is always appended
11
+ * so the common case (`scripts lint --fix`, `scripts lint
12
+ * --max-warnings 0`, `scripts lint -- --fix`) Just Works. Consumers
13
+ * that need a different target should call `eslint` directly from
14
+ * `node_modules/.bin`.
15
+ */
16
+ const eslintBin = resolveBin('eslint');
17
+
18
+ const userArgs = process.argv.slice(2);
19
+
20
+ const args = buildWrapperArgs(userArgs, {
21
+ defaults: ['src', '--quiet'],
22
+ target: 'src',
23
+ });
24
+
25
+ const result = spawn.sync(process.execPath, [eslintBin, ...args], { stdio: 'inherit' });
26
+
27
+ process.exit(result.status ?? 1);
@@ -0,0 +1,27 @@
1
+ import spawn from 'cross-spawn';
2
+
3
+ import { buildWrapperArgs } from '../utils/buildWrapperArgs';
4
+ import { resolveBin } from '../utils/resolveBin';
5
+
6
+ /**
7
+ * `scripts prettier` — `--check` over `src/**\/*.{ts,tsx,json,css,md}`.
8
+ *
9
+ * Flag args pass through; the default glob is always appended so
10
+ * `scripts prettier --write` Just Works. Consumers that need a
11
+ * different target should call `prettier` directly from
12
+ * `node_modules/.bin`.
13
+ */
14
+ const prettierBin = resolveBin('prettier');
15
+
16
+ const DEFAULT_GLOB = 'src/**/*.{ts,tsx,json,css,md}';
17
+
18
+ const userArgs = process.argv.slice(2);
19
+
20
+ const args = buildWrapperArgs(userArgs, {
21
+ defaults: ['--check', DEFAULT_GLOB],
22
+ target: DEFAULT_GLOB,
23
+ });
24
+
25
+ const result = spawn.sync(process.execPath, [prettierBin, ...args], { stdio: 'inherit' });
26
+
27
+ process.exit(result.status ?? 1);