@apollion-dsi/scripts 0.8.1 → 0.9.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 (56) hide show
  1. package/CHANGELOG.md +41 -0
  2. package/coverage/clover.xml +21 -5
  3. package/coverage/coverage-final.json +2 -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/checkRequiredFiles.ts.html +1 -1
  10. package/coverage/lcov-report/utils/formatWebpackMessages.ts.html +1 -1
  11. package/coverage/lcov-report/utils/getPublicUrlOrPath.ts.html +1 -1
  12. package/coverage/lcov-report/utils/index.html +23 -8
  13. package/coverage/lcov-report/utils/resolveBin.ts.html +193 -0
  14. package/coverage/lcov.info +34 -2
  15. package/eslint.config.js +4 -4
  16. package/lib/bin.js +8 -3
  17. package/lib/bin.js.map +1 -1
  18. package/lib/command/audit.d.ts +1 -0
  19. package/lib/command/audit.js +44 -0
  20. package/lib/command/audit.js.map +1 -0
  21. package/lib/command/check-types.d.ts +1 -0
  22. package/lib/command/check-types.js +18 -0
  23. package/lib/command/check-types.js.map +1 -0
  24. package/lib/command/create/helper.js +0 -1
  25. package/lib/command/create/helper.js.map +1 -1
  26. package/lib/command/create/index.js +7 -5
  27. package/lib/command/create/index.js.map +1 -1
  28. package/lib/command/lint.d.ts +1 -0
  29. package/lib/command/lint.js +31 -0
  30. package/lib/command/lint.js.map +1 -0
  31. package/lib/command/prettier.d.ts +1 -0
  32. package/lib/command/prettier.js +31 -0
  33. package/lib/command/prettier.js.map +1 -0
  34. package/lib/command/validate.d.ts +1 -0
  35. package/lib/command/validate.js +51 -0
  36. package/lib/command/validate.js.map +1 -0
  37. package/lib/utils/resolveBin.d.ts +13 -0
  38. package/lib/utils/resolveBin.js +35 -0
  39. package/lib/utils/resolveBin.js.map +1 -0
  40. package/llms.txt +51 -2
  41. package/package.json +9 -4
  42. package/src/__tests__/resolveBin.test.ts +68 -0
  43. package/src/bin.ts +11 -3
  44. package/src/command/audit.ts +46 -0
  45. package/src/command/check-types.ts +18 -0
  46. package/src/command/create/helper.ts +0 -1
  47. package/src/command/create/index.ts +7 -5
  48. package/src/command/lint.ts +31 -0
  49. package/src/command/prettier.ts +32 -0
  50. package/src/command/validate.ts +66 -0
  51. package/src/utils/resolveBin.ts +36 -0
  52. package/template/src/App.tsx +5 -2
  53. package/template/src/index.tsx +8 -6
  54. package/template/tsconfig.json +3 -24
  55. package/tsconfig.base.json +19 -0
  56. package/template/audit-ci.json +0 -5
@@ -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,31 @@ 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 src/`, `scripts check-types -- -p tsconfig.build.json`, etc.
26
+
27
+ Use via `package.json#scripts` — the whole consumer file becomes thin:
20
28
 
21
29
  ```json
22
- { "scripts": { "dev": "scripts dev", "build": "scripts build", "test": "scripts test" } }
30
+ {
31
+ "scripts": {
32
+ "dev": "scripts dev",
33
+ "build": "scripts build",
34
+ "test": "scripts test",
35
+ "lint": "scripts lint",
36
+ "lint:fix": "scripts lint -- --fix",
37
+ "prettier": "scripts prettier",
38
+ "check-types": "scripts check-types",
39
+ "audit-dependencies": "scripts audit",
40
+ "validate": "scripts validate"
41
+ }
42
+ }
23
43
  ```
24
44
 
25
45
  ## Stack (hidden from consumer)
@@ -28,19 +48,48 @@ Use via `package.json#scripts`:
28
48
  - `babel-plugin-relay` + `babel-plugin-styled-components`
29
49
  - `react-refresh` for HMR
30
50
  - Jest 30 + ts-jest + `@testing-library/react`
51
+ - ESLint 9 (`@apollion-dsi/eslint-config`) + Prettier 3
31
52
  - Peer: **TypeScript 6.x**
32
53
 
54
+ Consumers pull *only* `@apollion-dsi/scripts` for tooling — eslint, prettier,
55
+ jest, babel et al. arrive transitively. The consumer's `package.json#scripts`
56
+ should only reference `scripts <cmd>`.
57
+
33
58
  ## Invariants
34
59
 
35
60
  * **Yarn only** (consumer template uses Yarn 4 Berry).
36
61
  * **No webpack config exposed.** If you need a config escape hatch,
37
62
  open a ROADMAP entry — the point of this package is to not have one.
38
63
  * **TypeScript 6 strict** in the template — non-negotiable.
64
+ * **Template tsconfig modern, never legacy.** `target: es2020`,
65
+ `moduleResolution: bundler`, `jsx: react-jsx`. No `es5`, no `node10`,
66
+ no classic JSX — both warn under TS 6.0 (`TS5107`) and break in TS 7.
67
+
68
+ ## tsconfig.base.json (consumer-facing)
69
+
70
+ `@apollion-dsi/scripts` exposes a modern TS base config. Consumers
71
+ extend it instead of copying values:
72
+
73
+ ```jsonc
74
+ // tsconfig.json
75
+ {
76
+ "extends": "@apollion-dsi/scripts/tsconfig.base.json",
77
+ "include": ["src"]
78
+ }
79
+ ```
80
+
81
+ The base sets `target: es2020`, `module: esnext`,
82
+ `moduleResolution: bundler`, `jsx: react-jsx` (automatic runtime — no
83
+ `import React` needed for JSX), `strict`, `isolatedModules`,
84
+ `noEmit`. Overriding any field is fine; the goal is a defaultful SSOT,
85
+ not a lock-in.
39
86
 
40
87
  ## Files
41
88
 
42
89
  - `lib/bin.js` — CLI entry (resolved by `package.json#bin`).
43
90
  - `lib/index.js` — programmatic entry.
91
+ - `tsconfig.base.json` — exported via `package.json#exports` for
92
+ consumer `extends`.
44
93
  - `src/` — TS sources.
45
94
  - `scripts/validate.sh` — workspace `validate` step.
46
95
 
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.0",
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,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,31 @@
1
+ import spawn from 'cross-spawn';
2
+
3
+ import { resolveBin } from '../utils/resolveBin';
4
+
5
+ /**
6
+ * `scripts lint` — ESLint over `src` with `--quiet`. ESLint 9 flat
7
+ * config uses `eslint.config.js#files` for filtering (no `--ext`).
8
+ *
9
+ * If the consumer passes any path-like arg, they own the invocation
10
+ * entirely. Flag-only invocations (e.g. `scripts lint --fix`) keep the
11
+ * default `src` target so the common case Just Works.
12
+ */
13
+ const eslintBin = resolveBin('eslint');
14
+
15
+ const userArgs = process.argv.slice(2);
16
+
17
+ const userProvidedPath = userArgs.some((a) => !a.startsWith('-'));
18
+
19
+ let args: string[];
20
+
21
+ if (userArgs.length === 0) {
22
+ args = ['src', '--quiet'];
23
+ } else if (userProvidedPath) {
24
+ args = userArgs;
25
+ } else {
26
+ args = [...userArgs, 'src'];
27
+ }
28
+
29
+ const result = spawn.sync(process.execPath, [eslintBin, ...args], { stdio: 'inherit' });
30
+
31
+ process.exit(result.status ?? 1);
@@ -0,0 +1,32 @@
1
+ import spawn from 'cross-spawn';
2
+
3
+ import { resolveBin } from '../utils/resolveBin';
4
+
5
+ /**
6
+ * `scripts prettier` — `--check` over `src/**\/*.{ts,tsx,json,css,md}`.
7
+ *
8
+ * Flag-only user args (e.g. `scripts prettier --write`) keep the default
9
+ * glob so `--write` is enough. A path-like arg switches to fully
10
+ * user-controlled invocation.
11
+ */
12
+ const prettierBin = resolveBin('prettier');
13
+
14
+ const DEFAULT_GLOB = 'src/**/*.{ts,tsx,json,css,md}';
15
+
16
+ const userArgs = process.argv.slice(2);
17
+
18
+ const userProvidedPath = userArgs.some((a) => !a.startsWith('-'));
19
+
20
+ let args: string[];
21
+
22
+ if (userArgs.length === 0) {
23
+ args = ['--check', DEFAULT_GLOB];
24
+ } else if (userProvidedPath) {
25
+ args = userArgs;
26
+ } else {
27
+ args = [...userArgs, DEFAULT_GLOB];
28
+ }
29
+
30
+ const result = spawn.sync(process.execPath, [prettierBin, ...args], { stdio: 'inherit' });
31
+
32
+ process.exit(result.status ?? 1);
@@ -0,0 +1,66 @@
1
+ import chalk from 'chalk';
2
+ import spawn from 'cross-spawn';
3
+
4
+ /**
5
+ * `scripts validate` — Apollion's standard pre-merge gate. Runs
6
+ * `check-types`, `prettier --check`, `lint` (parallel) then `test`
7
+ * with `--watchAll=false` (sequential). Bails on the first failure.
8
+ *
9
+ * Mirrors the Apollion ecosystem's own `scripts/validate.sh`
10
+ * orchestration so consumers don't have to reinvent it.
11
+ */
12
+ const userArgs = process.argv.slice(2);
13
+
14
+ const skip = new Set(
15
+ userArgs.filter((arg) => arg.startsWith('--skip=')).flatMap((arg) => arg.slice('--skip='.length).split(',')),
16
+ );
17
+
18
+ type Step = { name: string; cmd: string };
19
+
20
+ const parallel: Step[] = [
21
+ { name: 'check-types', cmd: 'check-types' },
22
+ { name: 'prettier', cmd: 'prettier' },
23
+ { name: 'lint', cmd: 'lint' },
24
+ ].filter((s) => !skip.has(s.name));
25
+
26
+ const sequential: Step[] = [{ name: 'test', cmd: 'test --watchAll=false' }].filter((s) => !skip.has(s.name));
27
+
28
+ function runStep(step: Step): Promise<number> {
29
+ return new Promise((resolve) => {
30
+ console.log(chalk.cyan(`▶ ${step.name}`));
31
+
32
+ const [bin, ...rest] = step.cmd.split(' ');
33
+
34
+ const child = spawn(process.execPath, [require.resolve(`./${bin}`), ...rest], {
35
+ stdio: 'inherit',
36
+ });
37
+
38
+ child.on('close', (code) => resolve(code ?? 1));
39
+ });
40
+ }
41
+
42
+ (async () => {
43
+ const parallelResults = await Promise.all(parallel.map(runStep));
44
+
45
+ const parallelFailures = parallel.filter((_, i) => parallelResults[i] !== 0);
46
+
47
+ if (parallelFailures.length > 0) {
48
+ console.error(chalk.red(`✘ validate failed: ${parallelFailures.map((s) => s.name).join(', ')}`));
49
+
50
+ process.exit(1);
51
+ }
52
+
53
+ for (const step of sequential) {
54
+ const code = await runStep(step);
55
+
56
+ if (code !== 0) {
57
+ console.error(chalk.red(`✘ validate failed: ${step.name}`));
58
+
59
+ process.exit(code);
60
+ }
61
+ }
62
+
63
+ console.log(chalk.green('✓ validate passed'));
64
+
65
+ process.exit(0);
66
+ })();
@@ -0,0 +1,36 @@
1
+ import { createRequire } from 'module';
2
+ import path from 'path';
3
+
4
+ /**
5
+ * Resolve the absolute path to a dependency's CLI entrypoint as declared
6
+ * in its `package.json#bin`. Works for packages whose `exports` map
7
+ * blocks `require.resolve('<pkg>/bin/...')` (e.g. eslint 9+).
8
+ *
9
+ * @param pkgName - dependency name (e.g. `'eslint'`, `'prettier'`)
10
+ * @param binName - bin name when `package.json#bin` is an object map.
11
+ * Defaults to `pkgName`.
12
+ * @param fromPath - directory to resolve `pkgName` from. Defaults to
13
+ * this file's directory so production calls walk up from
14
+ * `@apollion-dsi/scripts`'s install location.
15
+ */
16
+ export function resolveBin(pkgName: string, binName: string = pkgName, fromPath: string = __dirname): string {
17
+ const req = createRequire(path.join(fromPath, 'noop.js'));
18
+
19
+ const pkgJsonPath = req.resolve(`${pkgName}/package.json`);
20
+
21
+ const pkgJson = req(pkgJsonPath) as { bin?: string | Record<string, string> };
22
+
23
+ const binField = pkgJson.bin;
24
+
25
+ if (!binField) {
26
+ throw new Error(`Package "${pkgName}" has no "bin" field in package.json`);
27
+ }
28
+
29
+ const relBin = typeof binField === 'string' ? binField : binField[binName];
30
+
31
+ if (!relBin) {
32
+ throw new Error(`Package "${pkgName}" has no bin named "${binName}"`);
33
+ }
34
+
35
+ return path.resolve(path.dirname(pkgJsonPath), relBin);
36
+ }
@@ -1,5 +1,8 @@
1
- import { ApollionProvider, Button, Flex, GlobalStyle, Text, useNotification } from '@apollion-dsi/core';
2
- import React from 'react';
1
+ import { Flex } from '@apollion-dsi/core/containers/flex';
2
+ import { useNotification } from '@apollion-dsi/core/containers/notification';
3
+ import { Button } from '@apollion-dsi/core/elements/button';
4
+ import { Text } from '@apollion-dsi/core/elements/text';
5
+ import { ApollionProvider, GlobalStyle } from '@apollion-dsi/core/themes';
3
6
 
4
7
  function App() {
5
8
  const { showNotification } = useNotification();
@@ -1,10 +1,12 @@
1
- import React from 'react';
2
- import { render } from 'react-dom';
1
+ import { StrictMode } from 'react';
2
+ import { createRoot } from 'react-dom/client';
3
3
  import { Root } from './App';
4
4
 
5
- render(
6
- <React.StrictMode>
5
+ const container = document.getElementById('root');
6
+ if (!container) throw new Error('Root container #root not found');
7
+
8
+ createRoot(container).render(
9
+ <StrictMode>
7
10
  <Root />
8
- </React.StrictMode>,
9
- document.getElementById('root'),
11
+ </StrictMode>,
10
12
  );
@@ -1,25 +1,4 @@
1
1
  {
2
- "compilerOptions": {
3
- "target": "es5",
4
- "lib": [
5
- "dom",
6
- "dom.iterable",
7
- "esnext"
8
- ],
9
- "allowJs": true,
10
- "skipLibCheck": true,
11
- "esModuleInterop": true,
12
- "allowSyntheticDefaultImports": true,
13
- "strict": true,
14
- "forceConsistentCasingInFileNames": true,
15
- "module": "esnext",
16
- "moduleResolution": "node",
17
- "resolveJsonModule": true,
18
- "isolatedModules": true,
19
- "noEmit": true,
20
- "jsx": "react"
21
- },
22
- "include": [
23
- "src"
24
- ]
25
- }
2
+ "extends": "@apollion-dsi/scripts/tsconfig.base.json",
3
+ "include": ["src"]
4
+ }
@@ -0,0 +1,19 @@
1
+ {
2
+ "$schema": "https://json.schemastore.org/tsconfig",
3
+ "compilerOptions": {
4
+ "target": "es2020",
5
+ "lib": ["dom", "dom.iterable", "esnext"],
6
+ "module": "esnext",
7
+ "moduleResolution": "bundler",
8
+ "jsx": "react-jsx",
9
+ "allowJs": true,
10
+ "skipLibCheck": true,
11
+ "esModuleInterop": true,
12
+ "allowSyntheticDefaultImports": true,
13
+ "resolveJsonModule": true,
14
+ "isolatedModules": true,
15
+ "forceConsistentCasingInFileNames": true,
16
+ "strict": true,
17
+ "noEmit": true
18
+ }
19
+ }
@@ -1,5 +0,0 @@
1
- {
2
- "high": true,
3
- "package-manager": "yarn",
4
- "report-type": "summary"
5
- }