@stjoseph/prumo 0.4.3 → 0.5.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.
- package/.prumo-templates/core/testing.md +14 -4
- package/README.md +23 -3
- package/dist/command.js +17 -0
- package/dist/compose.js +4 -1
- package/dist/doctor.js +18 -11
- package/dist/generate.js +2 -4
- package/dist/names.js +13 -1
- package/dist/pnpm-extended.js +159 -0
- package/package.json +5 -2
- package/templates/api/.gitattributes +2 -0
- package/templates/api/README.md +2 -0
- package/templates/api/scripts/database.mjs +2 -1
- package/templates/api/scripts/ports.mjs +249 -36
- package/templates/mobile/.gitattributes +2 -0
- package/templates/mobile/scripts/ports.mjs +249 -36
- package/templates/mobile/test/features/auth/authenticated-layout.spec.tsx +3 -2
- package/templates/mobile/test/features/auth/email-flows.spec.tsx +3 -2
- package/templates/mobile/test/features/auth/sign-in-form.spec.tsx +3 -2
- package/templates/mobile/test/features/auth/sign-out-button.spec.tsx +3 -2
- package/templates/mobile/test/features/auth/social-buttons.spec.tsx +3 -2
- package/templates/mobile/test/features/profile/profile-form.spec.tsx +3 -4
- package/templates/mobile/test/support/query-client.ts +14 -0
- package/templates/site/.gitattributes +2 -0
- package/templates/site/scripts/ports.mjs +249 -36
- package/templates/web/.gitattributes +2 -0
- package/templates/web/scripts/ports.mjs +249 -36
- package/templates/web/test/features/oauth/consent-form.spec.tsx +3 -2
- package/templates/web/test/features/profile/profile-form.spec.tsx +3 -2
- package/templates/web/test/support/query-client.ts +14 -0
- package/templates/web/test/support/render-app.tsx +2 -2
- package/templates/web/vite.config.ts +10 -3
- package/templates/workspace/.gitattributes +2 -0
- package/templates/workspace/README.md +2 -0
- package/templates/workspace/scripts/ports.mjs +249 -36
|
@@ -15,7 +15,8 @@ Do not mock `EntityManager` or any concrete class from a library.
|
|
|
15
15
|
|
|
16
16
|
**In a client, replace the network through the injected transport**: pass a fake `fetch` to `createClient`.
|
|
17
17
|
Do not add a request-mocking library. Test a component that shows an error by rendering it and asserting
|
|
18
|
-
**where** the error appears.
|
|
18
|
+
**where** the error appears. Build every test's `QueryClient` with `createTestQueryClient()` from
|
|
19
|
+
`@test/support/query-client`, never with `new QueryClient()`: the defaults a test needs live there, once.
|
|
19
20
|
|
|
20
21
|
Measure coverage and report it. Do not block a merge on a coverage threshold.
|
|
21
22
|
|
|
@@ -59,9 +60,18 @@ written in a mobile test is a mistake an assistant will make.
|
|
|
59
60
|
is **asynchronous** and its matchers need no setup. `expo-secure-store` needs the native Keychain or Keystore,
|
|
60
61
|
which Jest does not have, so a test builds the auth client with an in-memory token store, which is why a form
|
|
61
62
|
asks only for the auth methods it calls. And `jest-expo` leaves the app manifest empty, so `expo-linking` cannot
|
|
62
|
-
know the app's scheme: a test that reaches `Linking.createURL` mocks it with the scheme it expects.
|
|
63
|
-
|
|
64
|
-
|
|
63
|
+
know the app's scheme: a test that reaches `Linking.createURL` mocks it with the scheme it expects. And a query
|
|
64
|
+
or mutation left without an observer once a test unmounts starts TanStack Query's five-minute garbage-collection
|
|
65
|
+
timer, which Jest's worker waits on until it is force-exited. `createTestQueryClient()` sets `gcTime: Infinity`
|
|
66
|
+
for both, so no timer starts, and `retry: false` for queries, so a failure shows at once: the two defaults TanStack
|
|
67
|
+
Query's testing guide gives. Vitest does not wait on the timer, and `web` uses the same helper so that one rule
|
|
68
|
+
holds in both.
|
|
69
|
+
|
|
70
|
+
**`web` turns route code splitting off under Vitest**, in `vite.config.ts`. Split, a route's component is a module
|
|
71
|
+
Vite transforms the first time a test renders it, inside the one second a `find*` query waits, and on a loaded CI
|
|
72
|
+
runner the first page of a test file missed it. The wait is not the problem and is not raised: with the transform
|
|
73
|
+
gone the same load passes, and past that load a longer wait fails the same tests anyway. **What it costs:** the
|
|
74
|
+
tests never load a route's lazy chunk; the build and its preview do.
|
|
65
75
|
|
|
66
76
|
A coverage threshold turns a proxy into a target. Whoever is below writes tests to raise the number rather
|
|
67
77
|
than to check anything; whoever is above stops thinking. With no database mocks a test costs more to write,
|
package/README.md
CHANGED
|
@@ -33,7 +33,16 @@ workspace holding four applications, without either one carrying rules it has no
|
|
|
33
33
|
## Requirements
|
|
34
34
|
|
|
35
35
|
Node 22.18 or later, pnpm 10.26 or later, and Docker for anything with an API. `prumo doctor` checks
|
|
36
|
-
all of it. Prumo runs on macOS and
|
|
36
|
+
all of it. Prumo runs on macOS, Linux and Windows.
|
|
37
|
+
|
|
38
|
+
On Windows it runs natively: the project lives in a Windows folder, and the Windows Node and pnpm run it.
|
|
39
|
+
Docker Desktop, with its WSL2 backend and set to Linux containers, is only where the database and the API's
|
|
40
|
+
tests run.
|
|
41
|
+
|
|
42
|
+
Inside a WSL distribution, with the distribution's own terminal, Node, pnpm and Docker, Prumo is on Linux, which is
|
|
43
|
+
supported, though not tested on WSL specifically. Mixing the two is not supported: Windows tools, Prumo Desktop
|
|
44
|
+
included, working on a project inside the WSL filesystem (`\\wsl$` or `\\wsl.localhost`).
|
|
45
|
+
|
|
37
46
|
|
|
38
47
|
## Running it
|
|
39
48
|
|
|
@@ -51,7 +60,9 @@ committed, so the first commit is yours and the diff shows everything Prumo wrot
|
|
|
51
60
|
writes into the current directory instead, which must be empty, and names the project after it. If
|
|
52
61
|
anything fails or is interrupted, Prumo removes what it wrote, so the same command can run again.
|
|
53
62
|
A project cannot be named `api`, `web`, `mobile` or `site`: those are the names its apps take inside
|
|
54
|
-
a workspace.
|
|
63
|
+
a workspace. Nor can it take the name of a package that pnpm's compatibility database extends, such as `ws`:
|
|
64
|
+
pnpm applies the extension to the project's own `package.json` and installs what it adds. Prumo names
|
|
65
|
+
the packages and suggests another name.
|
|
55
66
|
|
|
56
67
|
Every question also has a flag, which is how CI runs it without a terminal. Outside an interactive
|
|
57
68
|
terminal every question that applies needs its flag, the layout of a single type included: a missing
|
|
@@ -79,7 +90,7 @@ npx @stjoseph/prumo new my-api --types api --alone --single-tenant --email
|
|
|
79
90
|
| `prumo new [name \| .]` | Generates a project |
|
|
80
91
|
| `prumo db` | Inside a project with an API: creates the development database in Docker and writes its URL into `.env` |
|
|
81
92
|
| `prumo clean` | Removes what the project needed only once, today the database setup, once the database exists. Only the Prumo version that generated the project cleans it |
|
|
82
|
-
| `prumo doctor` | Checks Node, pnpm, git and Docker on this machine |
|
|
93
|
+
| `prumo doctor` | Checks Node, pnpm, git and Docker on this machine, and that Docker runs Linux containers |
|
|
83
94
|
| `prumo version` · `--version` · `-v` | Prints the CLI version |
|
|
84
95
|
| `prumo help [command]` · `--help` | Lists the commands, or describes one |
|
|
85
96
|
|
|
@@ -129,6 +140,7 @@ Once written, those documents belong to the team. Prumo does not come back and r
|
|
|
129
140
|
| [`docs/structure.md`](https://github.com/stjosephworks/prumo/blob/main/docs/structure.md) | The `.prumo/` layout, and the rule deciding which areas a project receives |
|
|
130
141
|
| [`docs/document-template.md`](https://github.com/stjosephworks/prumo/blob/main/docs/document-template.md) | The shape every convention document fills |
|
|
131
142
|
| [`docs/maintaining-templates.md`](https://github.com/stjosephworks/prumo/blob/main/docs/maintaining-templates.md) | How a template is changed, and how the change is proved |
|
|
143
|
+
| [`docs/upgrading.md`](https://github.com/stjosephworks/prumo/blob/main/docs/upgrading.md) | What each release changed in the files it generates, and how an existing project carries it over |
|
|
132
144
|
|
|
133
145
|
## Releasing
|
|
134
146
|
|
|
@@ -136,6 +148,14 @@ Once written, those documents belong to the team. Prumo does not come back and r
|
|
|
136
148
|
registry does not have yet, the `publish` job sends it to npm through npm's trusted publishing, with provenance and
|
|
137
149
|
without a token in the repository. Any other push to `main` publishes nothing.
|
|
138
150
|
|
|
151
|
+
## Upgrading a project
|
|
152
|
+
|
|
153
|
+
Prumo does not update a project it generated, and there is no `prumo upgrade`. For each release that changed
|
|
154
|
+
the generated files, [`docs/upgrading.md`](https://github.com/stjosephworks/prumo/blob/main/docs/upgrading.md)
|
|
155
|
+
lists what to copy or change and why, and gives one method: generate a reference project with the same answers
|
|
156
|
+
at the old and the new version, and diff the files listed. A file your team has edited is merged by hand, never
|
|
157
|
+
overwritten.
|
|
158
|
+
|
|
139
159
|
## Status
|
|
140
160
|
|
|
141
161
|
Prumo is in beta: every template runs and every generated project is verified in CI, and what a generated project looks like can still change before 1.0.
|
package/dist/command.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { spawnSync } from 'node:child_process';
|
|
2
|
+
const windows = process.platform === 'win32';
|
|
3
|
+
// Only what the CLI itself writes: no space, quote or shell operator, so one line means the same in any shell.
|
|
4
|
+
const PLAIN = /^[\w@.:/={}-]+$/;
|
|
5
|
+
// Runs a tool the CLI names, such as pnpm, git or docker. On Windows pnpm is a .cmd shim, which Node starts only
|
|
6
|
+
// through a shell, and a shell given an argument list is deprecated (DEP0190), so there the command goes as one line.
|
|
7
|
+
export function runCommand(command, args, options = {}) {
|
|
8
|
+
if (!windows) {
|
|
9
|
+
return spawnSync(command, args, options);
|
|
10
|
+
}
|
|
11
|
+
for (const part of [command, ...args]) {
|
|
12
|
+
if (!PLAIN.test(part)) {
|
|
13
|
+
throw new Error(`${part} cannot be passed through a shell unquoted`);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
return spawnSync([command, ...args].join(' '), { ...options, shell: true });
|
|
17
|
+
}
|
package/dist/compose.js
CHANGED
|
@@ -49,7 +49,10 @@ export async function copyTemplate(source, destination) {
|
|
|
49
49
|
async function copyApp(templates, target, type) {
|
|
50
50
|
const destination = join(target, 'apps', type);
|
|
51
51
|
await copyTemplate(join(templates, type), destination);
|
|
52
|
-
|
|
52
|
+
// The workspace root carries both, for every app below it.
|
|
53
|
+
for (const rootOnly of ['.githooks', '.gitattributes']) {
|
|
54
|
+
await rm(join(destination, rootOnly), { recursive: true, force: true });
|
|
55
|
+
}
|
|
53
56
|
return destination;
|
|
54
57
|
}
|
|
55
58
|
async function contractFiles(directory) {
|
package/dist/doctor.js
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { runCommand } from "./command.js";
|
|
2
2
|
const NODE_FLOOR = [22, 18, 0];
|
|
3
3
|
// Every generated workspace approves dependency builds with `allowBuilds`, which pnpm added in 10.26.0.
|
|
4
4
|
const PNPM_FLOOR = [10, 26, 0];
|
|
5
|
-
// On Windows pnpm is a .cmd shim, and Node starts one only through a shell.
|
|
6
|
-
export const SHELL = process.platform === 'win32';
|
|
7
5
|
export const systemProbe = {
|
|
8
6
|
nodeVersion: process.versions.node,
|
|
9
7
|
run: (command, args) => {
|
|
10
|
-
const result =
|
|
11
|
-
return {
|
|
8
|
+
const result = runCommand(command, args, { encoding: 'utf8', timeout: 10_000 });
|
|
9
|
+
return {
|
|
10
|
+
ok: result.error === undefined && result.status === 0,
|
|
11
|
+
stdout: String(result.stdout ?? ''),
|
|
12
|
+
};
|
|
12
13
|
},
|
|
13
14
|
};
|
|
14
15
|
function atLeast(version, floor) {
|
|
@@ -59,19 +60,25 @@ export async function doctor(probe = systemProbe) {
|
|
|
59
60
|
});
|
|
60
61
|
checks.push(...toolChecks(probe, ['pnpm', 'git']));
|
|
61
62
|
const docker = probe.run('docker', ['--version']);
|
|
62
|
-
const
|
|
63
|
+
const engine = docker.ok ? probe.run('docker', ['info', '--format', '{{.OSType}}']) : undefined;
|
|
64
|
+
const os = firstLine(engine?.stdout ?? '');
|
|
63
65
|
checks.push({
|
|
64
66
|
id: 'docker',
|
|
65
67
|
label: 'Docker',
|
|
66
68
|
required: false,
|
|
67
|
-
...(
|
|
68
|
-
? {
|
|
69
|
-
: {
|
|
69
|
+
...(engine?.ok !== true
|
|
70
|
+
? {
|
|
70
71
|
status: 'warn',
|
|
71
72
|
detail: docker.ok
|
|
72
73
|
? 'installed but not running; open Docker Desktop. The API runs its database and tests in it'
|
|
73
|
-
: 'not found; install Docker Desktop. The API runs its database and tests in it',
|
|
74
|
-
}
|
|
74
|
+
: 'not found; install Docker Desktop, on Windows with the WSL2 backend. The API runs its database and tests in it',
|
|
75
|
+
}
|
|
76
|
+
: os !== 'linux'
|
|
77
|
+
? {
|
|
78
|
+
status: 'warn',
|
|
79
|
+
detail: `running ${os || 'unknown'} containers; switch Docker Desktop to Linux containers. The API's Postgres image is a Linux one`,
|
|
80
|
+
}
|
|
81
|
+
: { status: 'ok', detail: firstLine(docker.stdout) }),
|
|
75
82
|
});
|
|
76
83
|
return { ready: checks.every((check) => !check.required || check.status === 'ok'), checks };
|
|
77
84
|
}
|
package/dist/generate.js
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
import { spawnSync } from 'node:child_process';
|
|
2
1
|
import { randomBytes } from 'node:crypto';
|
|
3
2
|
import { existsSync } from 'node:fs';
|
|
4
3
|
import { readdir, readFile, rm, writeFile } from 'node:fs/promises';
|
|
5
4
|
import { join } from 'node:path';
|
|
5
|
+
import { runCommand } from "./command.js";
|
|
6
6
|
import { composeWorkspace, copyTemplate } from "./compose.js";
|
|
7
7
|
import { writeContext } from "./context.js";
|
|
8
|
-
import { SHELL } from "./doctor.js";
|
|
9
8
|
import { applyFeatures, enabledFor } from "./features.js";
|
|
10
9
|
import { setJsonc } from "./jsonc.js";
|
|
11
10
|
import { schemeFor, targetProblem } from "./names.js";
|
|
@@ -78,10 +77,9 @@ function stopIfInterrupted() {
|
|
|
78
77
|
function run(command, args, cwd, output) {
|
|
79
78
|
stopIfInterrupted();
|
|
80
79
|
// Under --json stdout belongs to the result document, so a child's output goes to stderr instead.
|
|
81
|
-
const result =
|
|
80
|
+
const result = runCommand(command, args, {
|
|
82
81
|
cwd,
|
|
83
82
|
stdio: output === 'inherit' ? 'inherit' : ['ignore', 2, 2],
|
|
84
|
-
shell: SHELL,
|
|
85
83
|
});
|
|
86
84
|
// spawnSync holds the event loop, so the listener has not run yet; the child says it instead, either by dying of
|
|
87
85
|
// the signal or, as pnpm does, by catching it and exiting with 130.
|
package/dist/names.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { readdirSync, statSync } from 'node:fs';
|
|
2
2
|
import { basename, resolve } from 'node:path';
|
|
3
|
+
import { EXTENDED_BY_PNPM } from "./pnpm-extended.js";
|
|
3
4
|
const PROJECT_NAME = /^[a-z][a-z0-9-]*$/;
|
|
4
5
|
// npm refuses a longer package name.
|
|
5
6
|
const MAX_LENGTH = 214;
|
|
@@ -15,7 +16,18 @@ export function validateProjectName(name) {
|
|
|
15
16
|
if (RESERVED.includes(name)) {
|
|
16
17
|
return 'That is the name an app takes inside a workspace; choose another.';
|
|
17
18
|
}
|
|
18
|
-
return
|
|
19
|
+
return extendedByPnpm(name);
|
|
20
|
+
}
|
|
21
|
+
function extendedByPnpm(name) {
|
|
22
|
+
const added = Object.hasOwn(EXTENDED_BY_PNPM, name) ? EXTENDED_BY_PNPM[name] : undefined;
|
|
23
|
+
if (added === undefined) {
|
|
24
|
+
return undefined;
|
|
25
|
+
}
|
|
26
|
+
const shown = added.length > 3
|
|
27
|
+
? `${added.slice(0, 3).join(', ')} and ${added.length - 3} more`
|
|
28
|
+
: added.join(', ');
|
|
29
|
+
return (`pnpm extends the npm package "${name}" with ${shown}, and would install them into a project of that name. ` +
|
|
30
|
+
`Choose another, such as "${name}-app".`);
|
|
19
31
|
}
|
|
20
32
|
// Why a directory cannot receive a project, or undefined when it can: it does not exist yet, or it is empty.
|
|
21
33
|
export function targetProblem(target) {
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
// pnpm applies a compatibility database, @yarnpkg/extensions, to every manifest it reads: the registry's packages and
|
|
2
|
+
// the project's own package.json alike. An entry matches by name and version range, and the template version 0.0.1
|
|
3
|
+
// falls inside ranges like `ws@<7.2.1`. A project carrying one of these names therefore gains the dependencies the
|
|
4
|
+
// entry adds, installed into its root, and a package among them with a build script stops `pnpm install` with
|
|
5
|
+
// ERR_PNPM_IGNORED_BUILDS. Each name maps to the packages it would add.
|
|
6
|
+
//
|
|
7
|
+
// Derived from @yarnpkg/extensions, pinned in devDependencies, by test/names.spec.ts, which fails when this list and
|
|
8
|
+
// the database part ways. Only names a project can take are kept, and only entries that add a package: one that sets
|
|
9
|
+
// nothing but peerDependenciesMeta changes no install.
|
|
10
|
+
export const EXTENDED_BY_PNPM = {
|
|
11
|
+
'apollo-upload-client': ['graphql'],
|
|
12
|
+
'auto-relay': ['reflect-metadata'],
|
|
13
|
+
'babel-plugin-graphql-tag': ['graphql'],
|
|
14
|
+
'babel-plugin-remove-graphql-queries': ['@babel/runtime', '@babel/types', 'gatsby-core-utils'],
|
|
15
|
+
'babel-plugin-transform-typescript-metadata': ['@babel/core', '@babel/traverse'],
|
|
16
|
+
'babel-preset-gatsby-package': ['@babel/runtime'],
|
|
17
|
+
'bin-links': ['mkdirp-infer-owner'],
|
|
18
|
+
'cacheable-lookup': ['@types/keyv'],
|
|
19
|
+
'clipanion-v3-codemod': ['jscodeshift'],
|
|
20
|
+
'connect-mongo': ['express-session'],
|
|
21
|
+
consolidate: [
|
|
22
|
+
'arc-templates',
|
|
23
|
+
'atpl',
|
|
24
|
+
'babel-core',
|
|
25
|
+
'bracket-template',
|
|
26
|
+
'coffee-script',
|
|
27
|
+
'dot',
|
|
28
|
+
'dust',
|
|
29
|
+
'dustjs-helpers',
|
|
30
|
+
'dustjs-linkedin',
|
|
31
|
+
'eco',
|
|
32
|
+
'ect',
|
|
33
|
+
'ejs',
|
|
34
|
+
'haml-coffee',
|
|
35
|
+
'hamlet',
|
|
36
|
+
'hamljs',
|
|
37
|
+
'handlebars',
|
|
38
|
+
'hogan.js',
|
|
39
|
+
'htmling',
|
|
40
|
+
'jade',
|
|
41
|
+
'jazz',
|
|
42
|
+
'jqtpl',
|
|
43
|
+
'just',
|
|
44
|
+
'liquid-node',
|
|
45
|
+
'liquor',
|
|
46
|
+
'lodash',
|
|
47
|
+
'marko',
|
|
48
|
+
'mote',
|
|
49
|
+
'mustache',
|
|
50
|
+
'nunjucks',
|
|
51
|
+
'plates',
|
|
52
|
+
'pug',
|
|
53
|
+
'qejs',
|
|
54
|
+
'ractive',
|
|
55
|
+
'razor-tmpl',
|
|
56
|
+
'react',
|
|
57
|
+
'react-dom',
|
|
58
|
+
'slm',
|
|
59
|
+
'squirrelly',
|
|
60
|
+
'swig',
|
|
61
|
+
'swig-templates',
|
|
62
|
+
'teacup',
|
|
63
|
+
'templayed',
|
|
64
|
+
'then-jade',
|
|
65
|
+
'then-pug',
|
|
66
|
+
'tinyliquid',
|
|
67
|
+
'toffee',
|
|
68
|
+
'twig',
|
|
69
|
+
'twing',
|
|
70
|
+
'underscore',
|
|
71
|
+
'vash',
|
|
72
|
+
'velocityjs',
|
|
73
|
+
'walrus',
|
|
74
|
+
'whiskers',
|
|
75
|
+
],
|
|
76
|
+
'cordova-ios': ['underscore'],
|
|
77
|
+
'cordova-lib': ['underscore'],
|
|
78
|
+
'create-gatsby': ['@babel/runtime'],
|
|
79
|
+
'elm-webpack-loader': ['temp'],
|
|
80
|
+
'eslint-import-resolver-vite': ['debug', 'resolve'],
|
|
81
|
+
fdir: ['picomatch'],
|
|
82
|
+
'fork-ts-checker-webpack-plugin': ['eslint', 'typescript', 'vue-template-compiler', 'webpack'],
|
|
83
|
+
'gatsby-admin': ['@babel/runtime'],
|
|
84
|
+
'gatsby-cli': ['@babel/runtime'],
|
|
85
|
+
'gatsby-core-utils': ['@babel/runtime', 'got'],
|
|
86
|
+
'gatsby-design-tokens': ['@babel/runtime'],
|
|
87
|
+
'gatsby-legacy-polyfills': ['@babel/runtime'],
|
|
88
|
+
'gatsby-plugin-benchmark-reporting': ['@babel/runtime'],
|
|
89
|
+
'gatsby-plugin-favicon': ['webpack'],
|
|
90
|
+
'gatsby-plugin-gatsby-cloud': ['gatsby-core-utils', 'webpack'],
|
|
91
|
+
'gatsby-plugin-graphql-config': ['@babel/runtime'],
|
|
92
|
+
'gatsby-plugin-i18n': ['ramda'],
|
|
93
|
+
'gatsby-plugin-image': ['@babel/runtime'],
|
|
94
|
+
'gatsby-plugin-mdx': ['@babel/runtime', 'mkdirp'],
|
|
95
|
+
'gatsby-plugin-netlify-cms': ['@babel/runtime'],
|
|
96
|
+
'gatsby-plugin-no-sourcemaps': ['@babel/runtime'],
|
|
97
|
+
'gatsby-plugin-page-creator': ['@babel/runtime', 'fs-extra'],
|
|
98
|
+
'gatsby-plugin-preact': ['@babel/runtime'],
|
|
99
|
+
'gatsby-plugin-preload-fonts': ['@babel/runtime'],
|
|
100
|
+
'gatsby-plugin-schema-snapshot': ['@babel/runtime'],
|
|
101
|
+
'gatsby-plugin-sharp': ['debug'],
|
|
102
|
+
'gatsby-plugin-styletron': ['@babel/runtime'],
|
|
103
|
+
'gatsby-plugin-subfont': ['@babel/runtime'],
|
|
104
|
+
'gatsby-plugin-utils': ['@babel/runtime', 'fastq', 'graphql'],
|
|
105
|
+
'gatsby-react-router-scroll': ['prop-types'],
|
|
106
|
+
'gatsby-recipes': ['@babel/runtime'],
|
|
107
|
+
'gatsby-remark-prismjs': ['lodash'],
|
|
108
|
+
'gatsby-source-apiserver': ['babel-polyfill'],
|
|
109
|
+
'gatsby-source-shopify': ['@babel/runtime'],
|
|
110
|
+
'gatsby-source-wikipedia': ['@babel/runtime'],
|
|
111
|
+
'gatsby-transformer-screenshot': ['@babel/runtime'],
|
|
112
|
+
'gatsby-worker': ['@babel/runtime'],
|
|
113
|
+
'git-node-fs': ['js-git'],
|
|
114
|
+
got: ['@types/keyv', '@types/responselike'],
|
|
115
|
+
'http-link-dataloader': ['graphql'],
|
|
116
|
+
'ink-select-input': ['react'],
|
|
117
|
+
'jest-vue-preprocessor': ['@babel/core', '@babel/template', 'pug'],
|
|
118
|
+
'jss-plugin-rule-value-function': ['tiny-warning'],
|
|
119
|
+
mqtt: ['duplexify'],
|
|
120
|
+
'query-ast': ['lodash'],
|
|
121
|
+
'rc-animate': ['react', 'react-dom'],
|
|
122
|
+
'react-bootstrap-table2-paginator': ['classnames'],
|
|
123
|
+
'react-color': ['react'],
|
|
124
|
+
'react-dev-utils': ['typescript', 'webpack'],
|
|
125
|
+
'react-draggable': ['react', 'react-dom'],
|
|
126
|
+
'react-github-btn': ['react'],
|
|
127
|
+
'react-instantsearch-core': ['algoliasearch'],
|
|
128
|
+
'react-instantsearch-dom': ['react-fast-compare'],
|
|
129
|
+
'react-live': ['react', 'react-dom'],
|
|
130
|
+
'react-portal': ['react-dom'],
|
|
131
|
+
'react-rnd': ['react', 'react-dom'],
|
|
132
|
+
'react-scripts': ['eslint', 'react'],
|
|
133
|
+
reactcss: ['react'],
|
|
134
|
+
rebass: ['react'],
|
|
135
|
+
'redux-persist': ['react'],
|
|
136
|
+
'redux-thunk': ['redux'],
|
|
137
|
+
'rollup-plugin-polyfill-node': ['rollup'],
|
|
138
|
+
'scss-parser': ['lodash'],
|
|
139
|
+
skypack: ['tar'],
|
|
140
|
+
snowpack: ['magic-string'],
|
|
141
|
+
terser: ['acorn'],
|
|
142
|
+
testcafe: ['@babel/plugin-transform-for-of', '@babel/runtime'],
|
|
143
|
+
'testcafe-legacy-api': ['read-file-relative', 'testcafe-hammerhead'],
|
|
144
|
+
'typescript-language-server': ['vscode-jsonrpc', 'vscode-languageserver-protocol'],
|
|
145
|
+
unified: ['@types/unist'],
|
|
146
|
+
'unplugin-vue2-script-setup': ['@vue/composition-api', '@vue/runtime-dom'],
|
|
147
|
+
'vite-plugin-vuetify': ['vue'],
|
|
148
|
+
'volar-service-typescript': ['typescript'],
|
|
149
|
+
'volar-service-typescript-twoslash-queries': ['typescript'],
|
|
150
|
+
'vue-cli-plugin-vuetify': ['null-loader', 'semver'],
|
|
151
|
+
'vue-i18n': ['vue'],
|
|
152
|
+
'vue-loader': ['@vue/compiler-sfc', 'webpack'],
|
|
153
|
+
'vue-router': ['vue'],
|
|
154
|
+
'vue-template-babel-compiler': ['vue-template-compiler'],
|
|
155
|
+
'webpack-plugin-vuetify': ['vue'],
|
|
156
|
+
'winston-transport': ['logform'],
|
|
157
|
+
ws: ['bufferutil', 'utf-8-validate'],
|
|
158
|
+
xo: ['webpack'],
|
|
159
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stjoseph/prumo",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"description": "Scaffolds a TypeScript project and ships it the conventions it follows, in a .prumo/ folder any AI assistant reads.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"scaffolding",
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"scripts": {
|
|
37
37
|
"lint": "biome check",
|
|
38
38
|
"typecheck": "tsc --noEmit",
|
|
39
|
-
"test": "vitest run --dir test",
|
|
39
|
+
"test": "vitest run --dir test --test-timeout 30000",
|
|
40
40
|
"prumo": "node src/cli.ts",
|
|
41
41
|
"build": "tsc -p tsconfig.build.json && node scripts/copy-assets.mjs",
|
|
42
42
|
"prepack": "pnpm build"
|
|
@@ -48,6 +48,9 @@
|
|
|
48
48
|
"devDependencies": {
|
|
49
49
|
"@biomejs/biome": "2.5.13",
|
|
50
50
|
"@types/node": "24.13.4",
|
|
51
|
+
"@types/semver": "7.8.0",
|
|
52
|
+
"@yarnpkg/extensions": "2.0.7",
|
|
53
|
+
"semver": "7.8.5",
|
|
51
54
|
"typescript": "6.0.3",
|
|
52
55
|
"vitest": "4.1.11"
|
|
53
56
|
},
|
package/templates/api/README.md
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
## Requirements
|
|
4
4
|
|
|
5
5
|
Node 22.18 or later, pnpm 10.26 or later, and Docker Desktop running: the database and the tests both run in it.
|
|
6
|
+
On Windows, Docker Desktop needs its WSL2 backend and Linux containers. Keep the project in a Windows folder, or
|
|
7
|
+
work entirely inside a WSL distribution; Windows tools on a project inside the WSL filesystem are not supported.
|
|
6
8
|
|
|
7
9
|
## Running it
|
|
8
10
|
|
|
@@ -267,7 +267,8 @@ async function main() {
|
|
|
267
267
|
|
|
268
268
|
if (!values['skip-migrate']) {
|
|
269
269
|
say('Migrating…')
|
|
270
|
-
|
|
270
|
+
// One line through the shell: on Windows pnpm is a .cmd shim, which Node starts only that way.
|
|
271
|
+
if (spawnSync('pnpm db:migrate', { cwd: api, stdio: childStdio, shell: true }).status !== 0) {
|
|
271
272
|
throw new Failure(
|
|
272
273
|
'migrate_failed',
|
|
273
274
|
'`pnpm db:migrate` failed. The database exists and .env points at it.',
|