@apollion-dsi/scripts 0.7.7 → 0.8.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.
- package/CHANGELOG.md +118 -0
- package/README.md +73 -0
- package/codemods/v4-migrate.mjs +196 -0
- package/coverage/clover.xml +123 -0
- package/coverage/coverage-final.json +7 -0
- package/coverage/lcov-report/base.css +224 -0
- package/coverage/lcov-report/block-navigation.js +87 -0
- package/coverage/lcov-report/config/env.ts.html +400 -0
- package/coverage/lcov-report/config/index.html +146 -0
- package/coverage/lcov-report/config/paths.ts.html +442 -0
- package/coverage/lcov-report/config/shared.ts.html +211 -0
- package/coverage/lcov-report/favicon.png +0 -0
- package/coverage/lcov-report/index.html +131 -0
- package/coverage/lcov-report/prettify.css +1 -0
- package/coverage/lcov-report/prettify.js +2 -0
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +210 -0
- package/coverage/lcov-report/utils/checkRequiredFiles.ts.html +178 -0
- package/coverage/lcov-report/utils/formatWebpackMessages.ts.html +214 -0
- package/coverage/lcov-report/utils/getPublicUrlOrPath.ts.html +259 -0
- package/coverage/lcov-report/utils/index.html +146 -0
- package/coverage/lcov.info +236 -0
- package/jest.config.js +36 -0
- package/lib/command/build.d.ts +14 -0
- package/lib/command/build.js +14 -0
- package/lib/command/build.js.map +1 -1
- package/lib/command/create/helper.js +0 -1
- package/lib/command/create/helper.js.map +1 -1
- package/lib/command/dev.d.ts +12 -0
- package/lib/command/dev.js +12 -0
- package/lib/command/dev.js.map +1 -1
- package/lib/config/env.d.ts +23 -0
- package/lib/config/env.js +23 -0
- package/lib/config/env.js.map +1 -1
- package/lib/config/paths.d.ts +31 -0
- package/lib/config/paths.js +11 -0
- package/lib/config/paths.js.map +1 -1
- package/lib/config/shared.d.ts +15 -0
- package/lib/config/shared.js +1 -0
- package/lib/config/shared.js.map +1 -1
- package/lib/index.d.ts +8 -0
- package/lib/index.js +8 -0
- package/lib/index.js.map +1 -1
- package/lib/utils/checkRequiredFiles.d.ts +9 -0
- package/lib/utils/checkRequiredFiles.js +9 -0
- package/lib/utils/checkRequiredFiles.js.map +1 -1
- package/lib/utils/formatWebpackMessages.d.ts +13 -0
- package/lib/utils/formatWebpackMessages.js +9 -0
- package/lib/utils/formatWebpackMessages.js.map +1 -1
- package/lib/utils/getPublicUrlOrPath.d.ts +19 -0
- package/lib/utils/getPublicUrlOrPath.js +19 -0
- package/lib/utils/getPublicUrlOrPath.js.map +1 -1
- package/llms.txt +52 -0
- package/package.json +9 -5
- package/scripts/validate.sh +3 -3
- package/src/__tests__/checkRequiredFiles.test.ts +44 -0
- package/src/__tests__/env.test.ts +51 -0
- package/src/__tests__/formatWebpackMessages.test.ts +42 -0
- package/src/__tests__/getPublicUrlOrPath.test.ts +38 -0
- package/src/__tests__/paths.test.ts +47 -0
- package/src/__tests__/shared.test.ts +14 -0
- package/src/command/build.ts +14 -0
- package/src/command/create/helper.ts +0 -1
- package/src/command/dev.ts +12 -0
- package/src/config/env.ts +23 -0
- package/src/config/paths.ts +31 -0
- package/src/config/shared.ts +15 -0
- package/src/index.ts +9 -0
- package/src/utils/checkRequiredFiles.ts +9 -0
- package/src/utils/formatWebpackMessages.ts +13 -0
- package/src/utils/getPublicUrlOrPath.ts +19 -0
- package/tsconfig.json +1 -1
- package/tsconfig.test.json +14 -0
- package/verify-no-install-scripts.js +76 -0
- package/.prettierrc.js +0 -1
- package/README.MD +0 -91
- package/audit-ci.json +0 -5
|
@@ -1 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Verify every file in `files` exists on disk. Logs the first missing
|
|
3
|
+
* file to `console.log` (with chalk) and returns `false`; otherwise
|
|
4
|
+
* returns `true`. Used by the build/dev commands to fail fast before
|
|
5
|
+
* touching webpack.
|
|
6
|
+
*
|
|
7
|
+
* @param files Absolute paths to required files (typically `paths.appHtml`,
|
|
8
|
+
* `paths.appIndexJs`).
|
|
9
|
+
*/
|
|
1
10
|
export default function checkRequiredFiles(files: string[]): boolean;
|
|
@@ -7,6 +7,15 @@ exports.default = checkRequiredFiles;
|
|
|
7
7
|
const fs_1 = __importDefault(require("fs"));
|
|
8
8
|
const path_1 = __importDefault(require("path"));
|
|
9
9
|
const chalk_1 = __importDefault(require("chalk"));
|
|
10
|
+
/**
|
|
11
|
+
* Verify every file in `files` exists on disk. Logs the first missing
|
|
12
|
+
* file to `console.log` (with chalk) and returns `false`; otherwise
|
|
13
|
+
* returns `true`. Used by the build/dev commands to fail fast before
|
|
14
|
+
* touching webpack.
|
|
15
|
+
*
|
|
16
|
+
* @param files Absolute paths to required files (typically `paths.appHtml`,
|
|
17
|
+
* `paths.appIndexJs`).
|
|
18
|
+
*/
|
|
10
19
|
function checkRequiredFiles(files) {
|
|
11
20
|
let currentFilePath;
|
|
12
21
|
try {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"checkRequiredFiles.js","sourceRoot":"","sources":["../../src/utils/checkRequiredFiles.ts"],"names":[],"mappings":";;;;;
|
|
1
|
+
{"version":3,"file":"checkRequiredFiles.js","sourceRoot":"","sources":["../../src/utils/checkRequiredFiles.ts"],"names":[],"mappings":";;;;;AAcA,qCAgBC;AA9BD,4CAAoB;AACpB,gDAAwB;AAExB,kDAA0B;AAE1B;;;;;;;;GAQG;AACH,SAAwB,kBAAkB,CAAC,KAAe;IACxD,IAAI,eAAmC,CAAC;IACxC,IAAI,CAAC;QACH,KAAK,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAE,EAAE;YACzB,eAAe,GAAG,QAAQ,CAAC;YAC3B,YAAE,CAAC,UAAU,CAAC,QAAQ,EAAE,YAAE,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;QAC7C,CAAC,CAAC,CAAC;QACH,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,OAAO,GAAG,eAAe,CAAC,CAAC,CAAC,cAAI,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACrE,MAAM,QAAQ,GAAG,eAAe,CAAC,CAAC,CAAC,cAAI,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACvE,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,GAAG,CAAC,iCAAiC,CAAC,CAAC,CAAC;QAC1D,OAAO,CAAC,GAAG,CAAC,KAAK,eAAK,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,eAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QAC/D,OAAO,CAAC,GAAG,CAAC,KAAK,eAAK,CAAC,GAAG,CAAC,aAAa,CAAC,KAAK,eAAK,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QACrE,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC"}
|
|
@@ -1,5 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Normalized result of {@link formatWebpackMessages} — `errors` and
|
|
3
|
+
* `warnings` are always string arrays (empty if absent in the input).
|
|
4
|
+
*/
|
|
1
5
|
export type FormattedMessages = {
|
|
2
6
|
errors: string[];
|
|
3
7
|
warnings: string[];
|
|
4
8
|
};
|
|
9
|
+
/**
|
|
10
|
+
* Coerce webpack's `stats.toJson({ warnings, errors })` shape into a
|
|
11
|
+
* plain `{ errors, warnings }` of strings. Tolerates `null`, arrays of
|
|
12
|
+
* objects with `{ message, moduleName }`, raw primitives, or anything
|
|
13
|
+
* else (falls back to `JSON.stringify` / `String()`).
|
|
14
|
+
*
|
|
15
|
+
* @param json Output of `stats.toJson({ warnings: true, errors: true })`
|
|
16
|
+
* or any partially-shaped equivalent.
|
|
17
|
+
*/
|
|
5
18
|
export default function formatWebpackMessages(json: any): FormattedMessages;
|
|
@@ -24,6 +24,15 @@ function toString(item) {
|
|
|
24
24
|
}
|
|
25
25
|
return String(item);
|
|
26
26
|
}
|
|
27
|
+
/**
|
|
28
|
+
* Coerce webpack's `stats.toJson({ warnings, errors })` shape into a
|
|
29
|
+
* plain `{ errors, warnings }` of strings. Tolerates `null`, arrays of
|
|
30
|
+
* objects with `{ message, moduleName }`, raw primitives, or anything
|
|
31
|
+
* else (falls back to `JSON.stringify` / `String()`).
|
|
32
|
+
*
|
|
33
|
+
* @param json Output of `stats.toJson({ warnings: true, errors: true })`
|
|
34
|
+
* or any partially-shaped equivalent.
|
|
35
|
+
*/
|
|
27
36
|
function formatWebpackMessages(json) {
|
|
28
37
|
const errors = Array.isArray(json?.errors) ? json.errors.map(toString) : [];
|
|
29
38
|
const warnings = Array.isArray(json?.warnings) ? json.warnings.map(toString) : [];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"formatWebpackMessages.js","sourceRoot":"","sources":["../../src/utils/formatWebpackMessages.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"formatWebpackMessages.js","sourceRoot":"","sources":["../../src/utils/formatWebpackMessages.ts"],"names":[],"mappings":";;AAsCA,wCAIC;AAjCD,SAAS,QAAQ,CAAC,IAAa;IAC7B,IAAI,IAAI,IAAI,IAAI;QAAE,OAAO,EAAE,CAAC;IAC5B,IAAI,OAAO,IAAI,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IAC1C,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC7B,MAAM,OAAO,GAAG,IAAW,CAAC;QAC5B,IAAI,OAAO,OAAO,CAAC,OAAO,KAAK,QAAQ,EAAE,CAAC;YACxC,IAAI,MAAM,GAAG,EAAE,CAAC;YAChB,IAAI,OAAO,CAAC,UAAU;gBAAE,MAAM,IAAI,MAAM,OAAO,CAAC,UAAU,MAAM,CAAC;YACjE,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC;YAC1B,OAAO,MAAM,CAAC;QAChB,CAAC;QACD,IAAI,CAAC;YACH,OAAO,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;QACjC,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,MAAM,CAAC,OAAO,CAAC,CAAC;QACzB,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC;AACtB,CAAC;AAED;;;;;;;;GAQG;AACH,SAAwB,qBAAqB,CAAC,IAAS;IACrD,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAC5E,MAAM,QAAQ,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAClF,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;AAC9B,CAAC"}
|
|
@@ -1 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compute the absolute base URL (or path) every bundled asset is served from.
|
|
3
|
+
*
|
|
4
|
+
* Precedence: `envPublicUrl` → `homepage` → `'/'`.
|
|
5
|
+
*
|
|
6
|
+
* In **development**, absolute URLs collapse to their pathname so the
|
|
7
|
+
* dev-server doesn't try to fetch from a different origin; relative
|
|
8
|
+
* homepages (starting with `.`) collapse to `'/'` for the same reason.
|
|
9
|
+
*
|
|
10
|
+
* In **production**, the full URL (or pathname when only `homepage` is
|
|
11
|
+
* set) is preserved so CDN-rooted bundles resolve correctly.
|
|
12
|
+
*
|
|
13
|
+
* Adapted from `react-scripts` — semantics kept identical to ease migration.
|
|
14
|
+
*
|
|
15
|
+
* @param isEnvDevelopment `true` when `NODE_ENV === 'development'`.
|
|
16
|
+
* @param homepage `package.json#homepage` (project-level).
|
|
17
|
+
* @param envPublicUrl `process.env.PUBLIC_URL` (per-invocation override).
|
|
18
|
+
* @returns A trailing-slash-normalized URL or absolute path.
|
|
19
|
+
*/
|
|
1
20
|
export default function getPublicUrlOrPath(isEnvDevelopment: boolean, homepage: string | undefined, envPublicUrl: string | undefined): string;
|
|
@@ -2,6 +2,25 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.default = getPublicUrlOrPath;
|
|
4
4
|
const url_1 = require("url");
|
|
5
|
+
/**
|
|
6
|
+
* Compute the absolute base URL (or path) every bundled asset is served from.
|
|
7
|
+
*
|
|
8
|
+
* Precedence: `envPublicUrl` → `homepage` → `'/'`.
|
|
9
|
+
*
|
|
10
|
+
* In **development**, absolute URLs collapse to their pathname so the
|
|
11
|
+
* dev-server doesn't try to fetch from a different origin; relative
|
|
12
|
+
* homepages (starting with `.`) collapse to `'/'` for the same reason.
|
|
13
|
+
*
|
|
14
|
+
* In **production**, the full URL (or pathname when only `homepage` is
|
|
15
|
+
* set) is preserved so CDN-rooted bundles resolve correctly.
|
|
16
|
+
*
|
|
17
|
+
* Adapted from `react-scripts` — semantics kept identical to ease migration.
|
|
18
|
+
*
|
|
19
|
+
* @param isEnvDevelopment `true` when `NODE_ENV === 'development'`.
|
|
20
|
+
* @param homepage `package.json#homepage` (project-level).
|
|
21
|
+
* @param envPublicUrl `process.env.PUBLIC_URL` (per-invocation override).
|
|
22
|
+
* @returns A trailing-slash-normalized URL or absolute path.
|
|
23
|
+
*/
|
|
5
24
|
function getPublicUrlOrPath(isEnvDevelopment, homepage, envPublicUrl) {
|
|
6
25
|
const stubDomain = 'https://create-react-app.dev';
|
|
7
26
|
if (envPublicUrl) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getPublicUrlOrPath.js","sourceRoot":"","sources":["../../src/utils/getPublicUrlOrPath.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"getPublicUrlOrPath.js","sourceRoot":"","sources":["../../src/utils/getPublicUrlOrPath.ts"],"names":[],"mappings":";;AAqBA,qCAoCC;AAzDD,6BAA0B;AAE1B;;;;;;;;;;;;;;;;;;GAkBG;AACH,SAAwB,kBAAkB,CACxC,gBAAyB,EACzB,QAA4B,EAC5B,YAAgC;IAEhC,MAAM,UAAU,GAAG,8BAA8B,CAAC;IAElD,IAAI,YAAY,EAAE,CAAC;QACjB,IAAI,UAAU,GAAG,YAAY,CAAC;QAC9B,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,GAAG,CAAC;YAAE,UAAU,IAAI,GAAG,CAAC;QACjD,IAAI,CAAC;YACH,MAAM,cAAc,GAAG,IAAI,SAAG,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;YACvD,OAAO,gBAAgB,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC;QACtG,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,UAAU,CAAC;QACpB,CAAC;IACH,CAAC;IAED,IAAI,QAAQ,EAAE,CAAC;QACb,IAAI,UAAU,GAAG,QAAQ,CAAC;QAC1B,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,GAAG,CAAC;YAAE,UAAU,IAAI,GAAG,CAAC;QACjD,IAAI,CAAC;YACH,MAAM,qBAAqB,GAAG,IAAI,SAAG,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC,QAAQ,CAAC;YACvE,OAAO,gBAAgB;gBACrB,CAAC,CAAC,UAAU,CAAC,UAAU,CAAC,GAAG,CAAC;oBAC1B,CAAC,CAAC,GAAG;oBACL,CAAC,CAAC,qBAAqB;gBACzB,CAAC,CAAC,UAAU,CAAC,UAAU,CAAC,GAAG,CAAC;oBAC1B,CAAC,CAAC,UAAU;oBACZ,CAAC,CAAC,qBAAqB,CAAC;QAC9B,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,UAAU,CAAC;QACpB,CAAC;IACH,CAAC;IAED,OAAO,GAAG,CAAC;AACb,CAAC"}
|
package/llms.txt
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Apollion DS — @apollion-dsi/scripts
|
|
2
|
+
|
|
3
|
+
Apollion's CRA-style CLI. Hides webpack 5 + Babel 7 + Jest 30 + TS 6
|
|
4
|
+
behind four commands: `create`, `dev`, `build`, `test`.
|
|
5
|
+
|
|
6
|
+
**Package:** `@apollion-dsi/scripts`. **Repo:**
|
|
7
|
+
`github.com/apollion-ds/apollion`. **Bin:** `scripts` (from
|
|
8
|
+
`lib/bin.js`).
|
|
9
|
+
|
|
10
|
+
## Commands
|
|
11
|
+
|
|
12
|
+
| `scripts <cmd>` | Effect |
|
|
13
|
+
|---|---|
|
|
14
|
+
| `create <name>` | Scaffold a new project from the template |
|
|
15
|
+
| `dev` | webpack-dev-server + HMR (react-refresh) at :3000 |
|
|
16
|
+
| `build` | Production bundle to `build/` |
|
|
17
|
+
| `test` | Jest 30 in watch mode against changed files |
|
|
18
|
+
|
|
19
|
+
Use via `package.json#scripts`:
|
|
20
|
+
|
|
21
|
+
```json
|
|
22
|
+
{ "scripts": { "dev": "scripts dev", "build": "scripts build", "test": "scripts test" } }
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Stack (hidden from consumer)
|
|
26
|
+
|
|
27
|
+
- webpack 5 + Babel 7 (`preset-env`, `preset-react`, `preset-typescript`)
|
|
28
|
+
- `babel-plugin-relay` + `babel-plugin-styled-components`
|
|
29
|
+
- `react-refresh` for HMR
|
|
30
|
+
- Jest 30 + ts-jest + `@testing-library/react`
|
|
31
|
+
- Peer: **TypeScript 6.x**
|
|
32
|
+
|
|
33
|
+
## Invariants
|
|
34
|
+
|
|
35
|
+
* **Yarn only** (consumer template uses Yarn 4 Berry).
|
|
36
|
+
* **No webpack config exposed.** If you need a config escape hatch,
|
|
37
|
+
open a ROADMAP entry — the point of this package is to not have one.
|
|
38
|
+
* **TypeScript 6 strict** in the template — non-negotiable.
|
|
39
|
+
|
|
40
|
+
## Files
|
|
41
|
+
|
|
42
|
+
- `lib/bin.js` — CLI entry (resolved by `package.json#bin`).
|
|
43
|
+
- `lib/index.js` — programmatic entry.
|
|
44
|
+
- `src/` — TS sources.
|
|
45
|
+
- `scripts/validate.sh` — workspace `validate` step.
|
|
46
|
+
|
|
47
|
+
## Not in scope
|
|
48
|
+
|
|
49
|
+
- Generic React boilerplate (template é opinionated p/ Apollion).
|
|
50
|
+
- Storybook setup (use `@apollion-dsi/core` Storybook diretamente).
|
|
51
|
+
- Monorepo orchestration (consumer monorepos usam yarn workspaces
|
|
52
|
+
diretamente).
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@apollion-dsi/scripts",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "Apollion Framework CLI",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"bin": "lib/bin.js",
|
|
7
7
|
"scripts": {
|
|
8
8
|
"prepare": "yarn build",
|
|
9
|
-
"audit-dependencies": "audit
|
|
9
|
+
"audit-dependencies": "yarn npm audit --severity moderate --no-deprecations",
|
|
10
10
|
"lint": "eslint src --quiet",
|
|
11
11
|
"lint:full": "eslint src",
|
|
12
12
|
"lint:fix": "eslint src --fix",
|
|
@@ -15,13 +15,16 @@
|
|
|
15
15
|
"format": "yarn prettier --write",
|
|
16
16
|
"validate": "./scripts/validate.sh",
|
|
17
17
|
"test": "yarn validate",
|
|
18
|
+
"test:watch": "jest --watch",
|
|
19
|
+
"coverage": "jest --coverage",
|
|
20
|
+
"validate:tests": "jest --coverage",
|
|
18
21
|
"build": "tsc -p ."
|
|
19
22
|
},
|
|
20
23
|
"keywords": [],
|
|
21
24
|
"author": "Apollion DS Team",
|
|
22
25
|
"license": "ISC",
|
|
23
26
|
"dependencies": {
|
|
24
|
-
"@apollion-dsi/eslint-config": "0.
|
|
27
|
+
"@apollion-dsi/eslint-config": "0.7.0",
|
|
25
28
|
"@babel/core": "7.29.0",
|
|
26
29
|
"@babel/preset-env": "7.29.5",
|
|
27
30
|
"@babel/preset-react": "7.28.5",
|
|
@@ -29,6 +32,7 @@
|
|
|
29
32
|
"@pmmmwh/react-refresh-webpack-plugin": "0.6.2",
|
|
30
33
|
"@testing-library/jest-dom": "6.9.1",
|
|
31
34
|
"@testing-library/react": "16.3.2",
|
|
35
|
+
"babel-jest": "^30.4.1",
|
|
32
36
|
"babel-loader": "10.1.1",
|
|
33
37
|
"babel-plugin-named-asset-import": "0.3.8",
|
|
34
38
|
"babel-plugin-relay": "20.1.1",
|
|
@@ -50,11 +54,12 @@
|
|
|
50
54
|
"inquirer": "13.4.3",
|
|
51
55
|
"jest": "30.4.2",
|
|
52
56
|
"jest-cli": "30.4.2",
|
|
57
|
+
"jest-environment-node": "^30.4.1",
|
|
53
58
|
"react-refresh": "0.18.0",
|
|
54
59
|
"resolve": "1.22.12",
|
|
55
60
|
"semver": "7.8.0",
|
|
56
61
|
"terser-webpack-plugin": "5.6.0",
|
|
57
|
-
"ts-jest": "29.4.
|
|
62
|
+
"ts-jest": "^29.4.11",
|
|
58
63
|
"tsconfig-paths-webpack-plugin": "4.2.0",
|
|
59
64
|
"update-notifier": "7.3.1",
|
|
60
65
|
"validate-npm-package-name": "8.0.0",
|
|
@@ -68,7 +73,6 @@
|
|
|
68
73
|
"devDependencies": {
|
|
69
74
|
"@testing-library/dom": "10.4.1",
|
|
70
75
|
"@types/jest": "30.0.0",
|
|
71
|
-
"audit-ci": "7.1.0",
|
|
72
76
|
"eslint": "9.29.0",
|
|
73
77
|
"npm-run-all": "4.1.5",
|
|
74
78
|
"prettier": "3.8.3",
|
package/scripts/validate.sh
CHANGED
|
@@ -4,13 +4,13 @@ BRANCH=$(git symbolic-ref --short HEAD)
|
|
|
4
4
|
|
|
5
5
|
if [ "$BRANCH" = "main" ]; then
|
|
6
6
|
echo "Validating on production"
|
|
7
|
-
npm-run-all --parallel check-types prettier lint && yarn build
|
|
7
|
+
npm-run-all --parallel check-types prettier lint && yarn validate:tests && yarn build
|
|
8
8
|
|
|
9
9
|
elif [ "$BRANCH" = "staging" ]; then
|
|
10
10
|
echo "Validating on staging"
|
|
11
|
-
npm-run-all --parallel check-types prettier lint && yarn build
|
|
11
|
+
npm-run-all --parallel check-types prettier lint && yarn validate:tests && yarn build
|
|
12
12
|
|
|
13
13
|
else
|
|
14
14
|
echo "Validating on development"
|
|
15
|
-
npm-run-all --parallel check-types prettier lint
|
|
15
|
+
npm-run-all --parallel check-types prettier lint && yarn validate:tests
|
|
16
16
|
fi
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import os from 'os';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
|
|
5
|
+
import checkRequiredFiles from '../utils/checkRequiredFiles';
|
|
6
|
+
|
|
7
|
+
describe('checkRequiredFiles', () => {
|
|
8
|
+
let tmpDir: string;
|
|
9
|
+
let logSpy: jest.SpyInstance;
|
|
10
|
+
|
|
11
|
+
beforeEach(() => {
|
|
12
|
+
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'apollion-scripts-'));
|
|
13
|
+
logSpy = jest.spyOn(console, 'log').mockImplementation(() => undefined);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
afterEach(() => {
|
|
17
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
18
|
+
logSpy.mockRestore();
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('returns true when every file exists', () => {
|
|
22
|
+
const fileA = path.join(tmpDir, 'a.txt');
|
|
23
|
+
const fileB = path.join(tmpDir, 'b.txt');
|
|
24
|
+
fs.writeFileSync(fileA, '');
|
|
25
|
+
fs.writeFileSync(fileB, '');
|
|
26
|
+
|
|
27
|
+
expect(checkRequiredFiles([fileA, fileB])).toBe(true);
|
|
28
|
+
expect(logSpy).not.toHaveBeenCalled();
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('returns false and logs the missing file when one is absent', () => {
|
|
32
|
+
const present = path.join(tmpDir, 'present.txt');
|
|
33
|
+
const missing = path.join(tmpDir, 'missing.txt');
|
|
34
|
+
fs.writeFileSync(present, '');
|
|
35
|
+
|
|
36
|
+
expect(checkRequiredFiles([present, missing])).toBe(false);
|
|
37
|
+
const logs = logSpy.mock.calls.flat().join(' ');
|
|
38
|
+
expect(logs).toContain('missing.txt');
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('returns true for an empty list', () => {
|
|
42
|
+
expect(checkRequiredFiles([])).toBe(true);
|
|
43
|
+
});
|
|
44
|
+
});
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import getClientEnvironment, { getEnviroment } from '../config/env';
|
|
2
|
+
|
|
3
|
+
describe('getClientEnvironment', () => {
|
|
4
|
+
const savedEnv = { ...process.env };
|
|
5
|
+
afterEach(() => {
|
|
6
|
+
process.env = { ...savedEnv };
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
it('exposes REACT_APP_* vars in raw and stringified', () => {
|
|
10
|
+
process.env.REACT_APP_API_URL = 'https://api.example.com';
|
|
11
|
+
process.env.SECRET_KEY = 'leak-me-not';
|
|
12
|
+
process.env.NODE_ENV = 'production';
|
|
13
|
+
|
|
14
|
+
const result = getClientEnvironment('/public/', { useFastRefresh: false });
|
|
15
|
+
const raw = result.raw as Record<string, unknown>;
|
|
16
|
+
|
|
17
|
+
expect(raw.REACT_APP_API_URL).toBe('https://api.example.com');
|
|
18
|
+
expect(raw.PUBLIC_URL).toBe('/public/');
|
|
19
|
+
expect(raw.NODE_ENV).toBe('production');
|
|
20
|
+
expect(raw.FAST_REFRESH).toBe(false);
|
|
21
|
+
|
|
22
|
+
expect(raw).not.toHaveProperty('SECRET_KEY');
|
|
23
|
+
expect((result.stringified['process.env'] as Record<string, string>).PUBLIC_URL).toBe('"/public/"');
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('defaults NODE_ENV to "development" when unset', () => {
|
|
27
|
+
delete process.env.NODE_ENV;
|
|
28
|
+
const result = getClientEnvironment('/', { useFastRefresh: true });
|
|
29
|
+
expect(result.raw.NODE_ENV).toBe('development');
|
|
30
|
+
expect(result.raw.FAST_REFRESH).toBe(true);
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
describe('getEnviroment', () => {
|
|
35
|
+
const savedEnv = { ...process.env };
|
|
36
|
+
afterEach(() => {
|
|
37
|
+
process.env = { ...savedEnv };
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('throws when NODE_ENV is unset', () => {
|
|
41
|
+
delete process.env.NODE_ENV;
|
|
42
|
+
expect(() => getEnviroment({ dotenv: '/tmp/nonexistent.env' } as never)).toThrow(
|
|
43
|
+
/NODE_ENV environment variable is required/,
|
|
44
|
+
);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('completes silently when no dotenv files exist', () => {
|
|
48
|
+
process.env.NODE_ENV = 'test';
|
|
49
|
+
expect(() => getEnviroment({ dotenv: '/tmp/definitely-missing.env' } as never)).not.toThrow();
|
|
50
|
+
});
|
|
51
|
+
});
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import formatWebpackMessages from '../utils/formatWebpackMessages';
|
|
2
|
+
|
|
3
|
+
describe('formatWebpackMessages', () => {
|
|
4
|
+
it('returns empty arrays when the input has no errors or warnings', () => {
|
|
5
|
+
expect(formatWebpackMessages(null)).toEqual({ errors: [], warnings: [] });
|
|
6
|
+
expect(formatWebpackMessages({})).toEqual({ errors: [], warnings: [] });
|
|
7
|
+
expect(formatWebpackMessages({ errors: 'not-array' })).toEqual({ errors: [], warnings: [] });
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
it('coerces string entries verbatim', () => {
|
|
11
|
+
expect(formatWebpackMessages({ errors: ['boom'], warnings: ['ouch'] })).toEqual({
|
|
12
|
+
errors: ['boom'],
|
|
13
|
+
warnings: ['ouch'],
|
|
14
|
+
});
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('prefixes object messages with their moduleName', () => {
|
|
18
|
+
expect(
|
|
19
|
+
formatWebpackMessages({
|
|
20
|
+
errors: [{ moduleName: 'src/a.ts', message: 'syntax error' }],
|
|
21
|
+
warnings: [{ message: 'unused export' }],
|
|
22
|
+
}),
|
|
23
|
+
).toEqual({
|
|
24
|
+
errors: ['in src/a.ts\n\nsyntax error'],
|
|
25
|
+
warnings: ['unused export'],
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('stringifies unknown object shapes', () => {
|
|
30
|
+
expect(formatWebpackMessages({ errors: [{ code: 42 }] })).toEqual({
|
|
31
|
+
errors: ['{"code":42}'],
|
|
32
|
+
warnings: [],
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('coerces primitives to string', () => {
|
|
37
|
+
expect(formatWebpackMessages({ errors: [42, true] })).toEqual({
|
|
38
|
+
errors: ['42', 'true'],
|
|
39
|
+
warnings: [],
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
});
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import getPublicUrlOrPath from '../utils/getPublicUrlOrPath';
|
|
2
|
+
|
|
3
|
+
describe('getPublicUrlOrPath', () => {
|
|
4
|
+
describe('with envPublicUrl', () => {
|
|
5
|
+
it('returns the URL with trailing slash in production', () => {
|
|
6
|
+
expect(getPublicUrlOrPath(false, undefined, 'https://cdn.example.com/app')).toBe('https://cdn.example.com/app/');
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
it('returns absolute pathname in development for absolute URL', () => {
|
|
10
|
+
expect(getPublicUrlOrPath(true, undefined, 'https://cdn.example.com/app/')).toBe('/app/');
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it('returns root in development when URL starts with "."', () => {
|
|
14
|
+
expect(getPublicUrlOrPath(true, undefined, './nested/')).toBe('/');
|
|
15
|
+
});
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
describe('with homepage only', () => {
|
|
19
|
+
it('returns the pathname in production for absolute homepage', () => {
|
|
20
|
+
expect(getPublicUrlOrPath(false, 'https://example.com/app/', undefined)).toBe('/app/');
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('returns root in development for absolute homepage', () => {
|
|
24
|
+
expect(getPublicUrlOrPath(true, 'https://example.com/app', undefined)).toBe('/app/');
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('returns relative homepage in production when starting with "."', () => {
|
|
28
|
+
expect(getPublicUrlOrPath(false, './app/', undefined)).toBe('./app/');
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
describe('with neither', () => {
|
|
33
|
+
it('returns root', () => {
|
|
34
|
+
expect(getPublicUrlOrPath(true, undefined, undefined)).toBe('/');
|
|
35
|
+
expect(getPublicUrlOrPath(false, undefined, undefined)).toBe('/');
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
});
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import os from 'os';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
|
|
5
|
+
describe('createAppPaths', () => {
|
|
6
|
+
let tmpDir: string;
|
|
7
|
+
let savedCwd: string;
|
|
8
|
+
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
tmpDir = fs.realpathSync(fs.mkdtempSync(path.join(os.tmpdir(), 'apollion-scripts-paths-')));
|
|
11
|
+
fs.writeFileSync(path.join(tmpDir, 'package.json'), JSON.stringify({ name: 'fixture', homepage: '/app/' }));
|
|
12
|
+
fs.writeFileSync(path.join(tmpDir, 'scriptsrc.js'), 'module.exports = { output: "dist" };');
|
|
13
|
+
fs.mkdirSync(path.join(tmpDir, 'src'));
|
|
14
|
+
fs.writeFileSync(path.join(tmpDir, 'src', 'index.tsx'), '// fixture entry');
|
|
15
|
+
fs.writeFileSync(path.join(tmpDir, 'src', 'setupTests.ts'), '// fixture setup');
|
|
16
|
+
savedCwd = process.cwd();
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
afterEach(() => {
|
|
20
|
+
process.chdir(savedCwd);
|
|
21
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
22
|
+
jest.resetModules();
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('resolves every well-known path against the supplied base', () => {
|
|
26
|
+
process.chdir(tmpDir);
|
|
27
|
+
const { createAppPaths } = require('../config/paths');
|
|
28
|
+
const result = createAppPaths(tmpDir);
|
|
29
|
+
|
|
30
|
+
expect(result.appPath).toBe(tmpDir);
|
|
31
|
+
expect(result.appPackageJson).toBe(path.join(tmpDir, 'package.json'));
|
|
32
|
+
expect(result.appBuild).toBe(path.join(tmpDir, 'dist'));
|
|
33
|
+
expect(result.appHtml).toBe(path.join(tmpDir, 'public/index.html'));
|
|
34
|
+
expect(result.appIndexJs).toBe(path.join(tmpDir, 'src/index.tsx'));
|
|
35
|
+
expect(result.testsSetup).toBe(path.join(tmpDir, 'src/setupTests.ts'));
|
|
36
|
+
expect(result.moduleFileExtensions).toContain('tsx');
|
|
37
|
+
expect(result.publicUrlOrPath).toBe('/app/');
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('falls back to a `.js` extension when no source file matches', () => {
|
|
41
|
+
fs.rmSync(path.join(tmpDir, 'src', 'index.tsx'));
|
|
42
|
+
process.chdir(tmpDir);
|
|
43
|
+
const { createAppPaths } = require('../config/paths');
|
|
44
|
+
const result = createAppPaths(tmpDir);
|
|
45
|
+
expect(result.appIndexJs).toBe(path.join(tmpDir, 'src/index.js'));
|
|
46
|
+
});
|
|
47
|
+
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { defaultDevConfig } from '../config/shared';
|
|
2
|
+
|
|
3
|
+
describe('defaultDevConfig', () => {
|
|
4
|
+
it('exposes the expected dev defaults', () => {
|
|
5
|
+
expect(defaultDevConfig).toEqual({
|
|
6
|
+
port: 3000,
|
|
7
|
+
host: '0.0.0.0',
|
|
8
|
+
head: { title: 'Apollion' },
|
|
9
|
+
constants: {},
|
|
10
|
+
i18nConfig: {},
|
|
11
|
+
useFastRefresh: true,
|
|
12
|
+
});
|
|
13
|
+
});
|
|
14
|
+
});
|
package/src/command/build.ts
CHANGED
|
@@ -95,6 +95,20 @@ function build(config: Configuration, previousFileSizes: unknown) {
|
|
|
95
95
|
});
|
|
96
96
|
}
|
|
97
97
|
|
|
98
|
+
/**
|
|
99
|
+
* Run a one-shot webpack production build for the consumer app at `paths`.
|
|
100
|
+
*
|
|
101
|
+
* Side effects:
|
|
102
|
+
* - Sets `process.env.BABEL_ENV` / `NODE_ENV` to `'production'`.
|
|
103
|
+
* - Empties `paths.appBuild` and copies `paths.appPublic` into it.
|
|
104
|
+
* - On failure: prints the formatted error and exits with code 1
|
|
105
|
+
* (or merely warns when `TSC_COMPILE_ON_ERROR=true`).
|
|
106
|
+
* - On `process.env.CI`-truthy runs, treats any webpack warning as a hard error.
|
|
107
|
+
*
|
|
108
|
+
* @param paths Resolved app paths — typically `createAppPaths()`.
|
|
109
|
+
* @param userConfig Optional override. Merged on top of {@link defaultDevConfig}.
|
|
110
|
+
* Pass `configureWebpack` here to mutate the final webpack config.
|
|
111
|
+
*/
|
|
98
112
|
export async function createProductionBuild(
|
|
99
113
|
paths: AppPaths,
|
|
100
114
|
userConfig: CustomConfigType = defaultDevConfig,
|
package/src/command/dev.ts
CHANGED
|
@@ -22,6 +22,18 @@ import createDevServerConfig from '../config/webpackDevServer.config';
|
|
|
22
22
|
import checkRequiredFiles from '../utils/checkRequiredFiles';
|
|
23
23
|
import { choosePort, createCompiler, prepareProxy, prepareUrls } from '../utils/WebpackDevServerUtils';
|
|
24
24
|
|
|
25
|
+
/**
|
|
26
|
+
* Boot a webpack-dev-server for the consumer app described by `paths`.
|
|
27
|
+
*
|
|
28
|
+
* Side effects:
|
|
29
|
+
* - Sets `process.env.BABEL_ENV` / `NODE_ENV` to `'development'`.
|
|
30
|
+
* - Picks an available port starting at `userConfig.port ?? 3000`.
|
|
31
|
+
* - Registers `SIGINT`/`SIGTERM` handlers that stop the server and exit.
|
|
32
|
+
*
|
|
33
|
+
* @param paths Resolved app paths — typically `createAppPaths()`.
|
|
34
|
+
* @param userConfig Optional override. Merged on top of {@link defaultDevConfig}.
|
|
35
|
+
* Pass `configureWebpack` here to mutate the final webpack config.
|
|
36
|
+
*/
|
|
25
37
|
export async function createDevServer(paths: AppPaths, userConfig: CustomConfigType = defaultDevConfig) {
|
|
26
38
|
const useCustomConfig = fs.existsSync(paths.appCustomConfig);
|
|
27
39
|
|
package/src/config/env.ts
CHANGED
|
@@ -3,6 +3,16 @@ import path from 'path';
|
|
|
3
3
|
|
|
4
4
|
import { CustomConfigType } from './shared';
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
* Load environment variables from `.env*` files in cascading order
|
|
8
|
+
* (`.env.<NODE_ENV>.local`, `.env.local`, `.env.<NODE_ENV>`, `.env`)
|
|
9
|
+
* and prepend the consumer's `NODE_PATH` entries with absolute paths.
|
|
10
|
+
*
|
|
11
|
+
* **Throws** when `process.env.NODE_ENV` is unset — required so the
|
|
12
|
+
* cascade can pick the right file. Skips `.env.local` for `NODE_ENV=test`.
|
|
13
|
+
*
|
|
14
|
+
* @param paths `AppPaths` (only `paths.dotenv` is read).
|
|
15
|
+
*/
|
|
6
16
|
export function getEnviroment(paths) {
|
|
7
17
|
const { NODE_ENV, ENV } = process.env;
|
|
8
18
|
|
|
@@ -43,6 +53,19 @@ export function getEnviroment(paths) {
|
|
|
43
53
|
.join(path.delimiter);
|
|
44
54
|
}
|
|
45
55
|
|
|
56
|
+
/**
|
|
57
|
+
* Pick the `REACT_APP_*` subset of `process.env`, augment it with the
|
|
58
|
+
* fixed `NODE_ENV`/`PUBLIC_URL`/`WDS_SOCKET_*`/`FAST_REFRESH` keys, and
|
|
59
|
+
* return both the raw object (for runtime consumers) and a stringified
|
|
60
|
+
* form ready for webpack's `DefinePlugin`.
|
|
61
|
+
*
|
|
62
|
+
* **Security:** vars NOT prefixed with `REACT_APP_` (e.g. `SECRET_KEY`)
|
|
63
|
+
* are excluded — this is the boundary that prevents server-only secrets
|
|
64
|
+
* from leaking into the client bundle.
|
|
65
|
+
*
|
|
66
|
+
* @param publicUrl `paths.publicUrlOrPath` — exposed as `PUBLIC_URL`.
|
|
67
|
+
* @param config Resolved `CustomConfigType` (only `useFastRefresh` is read).
|
|
68
|
+
*/
|
|
46
69
|
export default function getClientEnvironment(publicUrl: string, config: CustomConfigType) {
|
|
47
70
|
// Grab NODE_ENV and REACT_APP_* environment variables and prepare them to be
|
|
48
71
|
// injected into the application via DefinePlugin in webpack configuration.
|