create-substrat 0.12.13 → 0.13.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/index.js +27 -10
- package/package.json +5 -2
- package/windows.js +90 -0
package/index.js
CHANGED
|
@@ -12,12 +12,13 @@
|
|
|
12
12
|
* break at install time and damage the name it exists to protect.
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
|
-
import { cpSync, existsSync, mkdirSync, readdirSync, writeFileSync } from 'node:fs';
|
|
15
|
+
import { cpSync, existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from 'node:fs';
|
|
16
16
|
import { basename, dirname, join, resolve } from 'node:path';
|
|
17
17
|
import { fileURLToPath } from 'node:url';
|
|
18
18
|
|
|
19
19
|
import { DEV_SERVERS } from './dev-servers.js';
|
|
20
20
|
import { TEST_RUNNER, TSCONFIG, VITEST_CONFIG } from './project-files.js';
|
|
21
|
+
import { offerLongPaths } from './windows.js';
|
|
21
22
|
|
|
22
23
|
const HERE = dirname(fileURLToPath(import.meta.url));
|
|
23
24
|
const TEMPLATE = join(HERE, 'template');
|
|
@@ -36,12 +37,12 @@ const TEMPLATE = join(HERE, 'template');
|
|
|
36
37
|
// The runtime packages release together off one version line (the changesets `fixed`
|
|
37
38
|
// group), so one constant is right for all of them. Engines do NOT share a line —
|
|
38
39
|
// each versions on its own, so one pin per engine, deliberately.
|
|
39
|
-
const SUBSTRAT = '^0.
|
|
40
|
-
const ENGINE_WORKORDER = '^0.12.
|
|
41
|
-
const ENGINE_INVOICING = '^0.11.
|
|
40
|
+
const SUBSTRAT = '^0.164.0';
|
|
41
|
+
const ENGINE_WORKORDER = '^0.12.48';
|
|
42
|
+
const ENGINE_INVOICING = '^0.11.48';
|
|
42
43
|
const BOUNDARY_LINT = '^0.8.0';
|
|
43
|
-
const DEV_ISSUER = '^0.2.
|
|
44
|
-
const ATTACHMENT_EXTRACTORS = '^0.2.
|
|
44
|
+
const DEV_ISSUER = '^0.2.46';
|
|
45
|
+
const ATTACHMENT_EXTRACTORS = '^0.2.25';
|
|
45
46
|
|
|
46
47
|
const DOCS = 'https://substrat.net';
|
|
47
48
|
|
|
@@ -70,7 +71,7 @@ function usage() {
|
|
|
70
71
|
function toPackageName(dir) {
|
|
71
72
|
const name = basename(resolve(dir))
|
|
72
73
|
.toLowerCase()
|
|
73
|
-
.replace(/[^a-z0-9
|
|
74
|
+
.replace(/[^a-z0-9-]+/g, '-')
|
|
74
75
|
.replace(/^[-_.]+|[-_.]+$/g, '');
|
|
75
76
|
return name || 'substrat-vertical';
|
|
76
77
|
}
|
|
@@ -138,7 +139,13 @@ function packageJson(name) {
|
|
|
138
139
|
// The local OIDC issuer `pnpm dev` signs you in at. A devDependency on
|
|
139
140
|
// purpose: it is never imported by anything the worker bundles.
|
|
140
141
|
'@substrat-run/dev-issuer': DEV_ISSUER,
|
|
141
|
-
|
|
142
|
+
// What `substrat push` builds the Worker with. The CLI runs the project's own
|
|
143
|
+
// wrangler and never downloads one, so a push builds with the version pinned here.
|
|
144
|
+
wrangler: '^4.151.0',
|
|
145
|
+
// Major 5, because wrangler 4.151+ peers on it: with 4 here, `npm install` of a new
|
|
146
|
+
// project stops on ERESOLVE. The template compiles against 5 (checked by installing a
|
|
147
|
+
// scaffold from npm); the workspace catalog, which template-check reads, is still on 4.
|
|
148
|
+
'@cloudflare/workers-types': '^5.20261023.1',
|
|
142
149
|
'@types/better-sqlite3': '^9.6.0',
|
|
143
150
|
'@types/node': '^22.0.0',
|
|
144
151
|
concurrently: '^9.0.0',
|
|
@@ -200,7 +207,7 @@ pnpm typecheck
|
|
|
200
207
|
`;
|
|
201
208
|
}
|
|
202
209
|
|
|
203
|
-
function main() {
|
|
210
|
+
async function main() {
|
|
204
211
|
const target = process.argv[2];
|
|
205
212
|
if (target === '-h' || target === '--help') {
|
|
206
213
|
usage();
|
|
@@ -227,6 +234,12 @@ function main() {
|
|
|
227
234
|
// Generated configs — these need the project name, so they aren't in the template.
|
|
228
235
|
const name = toPackageName(target);
|
|
229
236
|
writeFileSync(join(dest, 'package.json'), packageJson(name));
|
|
237
|
+
// The gate key is the project's registry slug (the package name): a vertical may declare only
|
|
238
|
+
// its own slug and the engines' keys (#2388), so the template's `bikeshop` becomes the name.
|
|
239
|
+
for (const file of ['src/manifest.ts', 'src/provision.ts']) {
|
|
240
|
+
const path = join(dest, file);
|
|
241
|
+
writeFileSync(path, readFileSync(path, 'utf8').replace(/(entitlementKey: |ENTITLEMENT_KEYS = \[[^\]]*)'bikeshop'/, `$1'${name}'`));
|
|
242
|
+
}
|
|
230
243
|
writeFileSync(join(dest, 'tsconfig.json'), TSCONFIG);
|
|
231
244
|
writeFileSync(join(dest, 'vitest.config.ts'), VITEST_CONFIG);
|
|
232
245
|
writeFileSync(join(dest, '.gitignore'), GITIGNORE);
|
|
@@ -250,6 +263,10 @@ function main() {
|
|
|
250
263
|
'',
|
|
251
264
|
].join('\n'),
|
|
252
265
|
);
|
|
266
|
+
|
|
267
|
+
// Windows only, and only when the setting is off: one question, then Windows' own prompt.
|
|
268
|
+
const longPaths = await offerLongPaths();
|
|
269
|
+
if (longPaths.length) process.stdout.write(`${longPaths.join('\n')}\n`);
|
|
253
270
|
}
|
|
254
271
|
|
|
255
|
-
main();
|
|
272
|
+
main().catch((error) => fail(error instanceof Error ? error.message : String(error)));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-substrat",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.0",
|
|
4
4
|
"description": "Scaffold a Substrat vertical — `npm create substrat <dir>`.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
"index.js",
|
|
27
27
|
"dev-servers.js",
|
|
28
28
|
"project-files.js",
|
|
29
|
+
"windows.js",
|
|
29
30
|
"template"
|
|
30
31
|
],
|
|
31
32
|
"engines": {
|
|
@@ -34,5 +35,7 @@
|
|
|
34
35
|
"publishConfig": {
|
|
35
36
|
"access": "public"
|
|
36
37
|
},
|
|
37
|
-
"scripts": {
|
|
38
|
+
"scripts": {
|
|
39
|
+
"test": "node --test windows.test.mjs"
|
|
40
|
+
}
|
|
38
41
|
}
|
package/windows.js
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
// Windows long paths, offered once when a project is scaffolded.
|
|
2
|
+
//
|
|
3
|
+
// A project's node_modules holds files deeper than Windows' old 260-character limit. Node, npm
|
|
4
|
+
// and pnpm go past it on their own; many other programs (PowerShell, Python, installers) stop
|
|
5
|
+
// there until the machine-wide `LongPathsEnabled` setting is on. Turning it on needs an
|
|
6
|
+
// administrator, so the scaffold asks first and then has Windows run the one registry write
|
|
7
|
+
// elevated: the person answers one question and Windows' own "allow this app" prompt.
|
|
8
|
+
// `substrat doctor --fix` (@substrat-run/cli, doctor.ts) does the same later; the two keep the
|
|
9
|
+
// same command and the same reading of `reg query`.
|
|
10
|
+
|
|
11
|
+
import { execFileSync } from 'node:child_process';
|
|
12
|
+
import { createInterface } from 'node:readline';
|
|
13
|
+
|
|
14
|
+
const FILESYSTEM_KEY = 'HKLM\\SYSTEM\\CurrentControlSet\\Control\\FileSystem';
|
|
15
|
+
|
|
16
|
+
/** Every argument is a constant: nothing a person or a project supplies reaches the shell. */
|
|
17
|
+
export const ELEVATE_LONG_PATHS_SCRIPT =
|
|
18
|
+
'Start-Process -FilePath reg.exe -Verb RunAs -Wait -WindowStyle Hidden -ArgumentList ' +
|
|
19
|
+
`'add','${FILESYSTEM_KEY}','/v','LongPathsEnabled','/t','REG_DWORD','/d','1','/f'`;
|
|
20
|
+
|
|
21
|
+
const LATER = 'npx @substrat-run/cli doctor --fix';
|
|
22
|
+
|
|
23
|
+
/** `reg query`'s answer: on, or off (an absent value is Windows' default, which is off). */
|
|
24
|
+
export function parseLongPaths(stdout) {
|
|
25
|
+
const m = stdout?.match(/LongPathsEnabled\s+REG_DWORD\s+0x([0-9a-f]+)/i);
|
|
26
|
+
return m ? parseInt(m[1], 16) !== 0 : false;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** One question on the terminal; `undefined` when input ends (or Ctrl+C) before an answer. */
|
|
30
|
+
function ask(question) {
|
|
31
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
32
|
+
return new Promise((resolve) => {
|
|
33
|
+
rl.question(question, (answer) => {
|
|
34
|
+
resolve(answer.trim());
|
|
35
|
+
rl.close();
|
|
36
|
+
});
|
|
37
|
+
rl.on('close', () => resolve(undefined));
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export const realSystem = () => ({
|
|
42
|
+
platform: process.platform,
|
|
43
|
+
interactive: Boolean(process.stdin.isTTY && process.stdout.isTTY),
|
|
44
|
+
query: () => {
|
|
45
|
+
try {
|
|
46
|
+
return execFileSync('reg.exe', ['query', FILESYSTEM_KEY, '/v', 'LongPathsEnabled'], {
|
|
47
|
+
encoding: 'utf8',
|
|
48
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
49
|
+
windowsHide: true,
|
|
50
|
+
});
|
|
51
|
+
} catch {
|
|
52
|
+
return undefined;
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
elevate: () => {
|
|
56
|
+
execFileSync('powershell.exe', ['-NoProfile', '-NonInteractive', '-Command', ELEVATE_LONG_PATHS_SCRIPT], {
|
|
57
|
+
stdio: ['ignore', 'ignore', 'pipe'],
|
|
58
|
+
windowsHide: true,
|
|
59
|
+
});
|
|
60
|
+
},
|
|
61
|
+
ask,
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* The lines to print after scaffolding, having offered to turn long paths on where that applies.
|
|
66
|
+
* Nothing on macOS and Linux, or when they are already on. Without a terminal to ask on (a coding
|
|
67
|
+
* agent) it changes nothing and says how to turn them on later. Never throws: a scaffold that
|
|
68
|
+
* has been written is not undone by a setting.
|
|
69
|
+
*/
|
|
70
|
+
export async function offerLongPaths(sys = realSystem()) {
|
|
71
|
+
if (sys.platform !== 'win32') return [];
|
|
72
|
+
if (parseLongPaths(sys.query())) return [];
|
|
73
|
+
const why = [
|
|
74
|
+
' Windows long paths are off. A project\'s node_modules holds files deeper than',
|
|
75
|
+
' 260 characters, and many programs stop at that limit until it is turned on.',
|
|
76
|
+
];
|
|
77
|
+
if (!sys.interactive) return [...why, ` Turn it on once (Windows asks you to allow it): ${LATER}`, ''];
|
|
78
|
+
const answer = await sys.ask([...why, ' Turn it on now? Windows will ask you to allow it. [Y/n] '].join('\n'));
|
|
79
|
+
if (answer === undefined || !(answer === '' || /^y/i.test(answer))) {
|
|
80
|
+
return ['', ` Left off. Turn it on later with: ${LATER}`, ''];
|
|
81
|
+
}
|
|
82
|
+
try {
|
|
83
|
+
sys.elevate();
|
|
84
|
+
} catch {
|
|
85
|
+
/* declined or failed: the re-read below says which */
|
|
86
|
+
}
|
|
87
|
+
return parseLongPaths(sys.query())
|
|
88
|
+
? ['', ' ✓ Windows long paths turned on. Restart your terminal and editor so they see it.', '']
|
|
89
|
+
: ['', ` Long paths are still off (the permission prompt was declined). Later: ${LATER}`, ''];
|
|
90
|
+
}
|