@cityholdings/minicube-dev 0.1.1 → 0.1.3
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/README.md +14 -6
- package/package.json +1 -1
- package/src/act.js +6 -7
- package/src/build.js +106 -25
- package/src/cli.js +1 -1
- package/src/config.js +0 -3
package/README.md
CHANGED
|
@@ -36,8 +36,17 @@ npm publish
|
|
|
36
36
|
|
|
37
37
|
Optional tools, used only by the commands that need them: `adb` (Android preview), Xcode
|
|
38
38
|
command-line tools (`xcrun`, iOS simulator/device preview), `kubectl` (token administration or
|
|
39
|
-
`transport: "kube"`),
|
|
40
|
-
|
|
39
|
+
`transport: "kube"`), `cube-pack` (inline card images), and the official mPaaS mini program IDE
|
|
40
|
+
(`mPaaS小程序开发者工具`).
|
|
41
|
+
|
|
42
|
+
Mini programs are compiled with the IDE's own `mini-pkg-builder` (and its `esbuild`), found
|
|
43
|
+
automatically in the default install locations: `/Applications` or `~/Applications` on macOS;
|
|
44
|
+
`%LOCALAPPDATA%\Programs`, `%ProgramFiles%` or the installer's recorded folder on Windows. No
|
|
45
|
+
configuration is needed; `builders.mini` / `MINICUBE_MINI_BUILDER` only override it.
|
|
46
|
+
|
|
47
|
+
Card projects must install ACT v4 themselves (`npm i -D @antdigital/cube@4`). minicube-dev uses only
|
|
48
|
+
that copy, found from the card directory upwards, and stops if it is missing or older than v4; a
|
|
49
|
+
global `act` (often ACT v2, which the apps cannot load) is never used.
|
|
41
50
|
|
|
42
51
|
## Configuration
|
|
43
52
|
|
|
@@ -59,16 +68,15 @@ by `MINICUBE_DEV_CONFIG` / `--config`. Create a skeleton with `minicube-dev conf
|
|
|
59
68
|
"devices": {
|
|
60
69
|
"android": { "package": "<android application id>", "cubeConnectActivity": "<debug activity accepting cubeActHost/cubeActPort>" },
|
|
61
70
|
"ios": { "bundleId": "<bundle id, physical devices only>" }
|
|
62
|
-
}
|
|
63
|
-
"builders": { "mini": "/path/to/mini-pkg-builder" }
|
|
71
|
+
}
|
|
64
72
|
}
|
|
65
73
|
```
|
|
66
74
|
|
|
67
75
|
* `token` or `tokenEnv` (name of an environment variable holding it) per environment.
|
|
68
76
|
* Without `baseUrl` (or with `"transport": "kube"`) requests go through `kubectl port-forward`
|
|
69
77
|
using your local kubeconfig.
|
|
70
|
-
* `builders
|
|
71
|
-
|
|
78
|
+
* Optional `"builders": { "mini": "<path>" }` (or `MINICUBE_MINI_BUILDER`) points at another
|
|
79
|
+
mini-pkg-builder executable or runtime `index.js`, e.g. when the IDE is installed elsewhere.
|
|
72
80
|
|
|
73
81
|
`minicube-dev doctor` checks the toolchain and every environment's token.
|
|
74
82
|
|
package/package.json
CHANGED
package/src/act.js
CHANGED
|
@@ -2,10 +2,10 @@ import { mkdirSync, openSync } from 'node:fs';
|
|
|
2
2
|
import { createConnection } from 'node:net';
|
|
3
3
|
import os from 'node:os';
|
|
4
4
|
import path from 'node:path';
|
|
5
|
-
import { cardDir, resolveAct } from './build.js';
|
|
5
|
+
import { cardDir, resolveAct, runAct } from './build.js';
|
|
6
6
|
import { adb, androidSerial } from './device.js';
|
|
7
7
|
import { CliError, log } from './log.js';
|
|
8
|
-
import { delay,
|
|
8
|
+
import { delay, runSync, spawnDetached } from './proc.js';
|
|
9
9
|
|
|
10
10
|
const DEFAULT_PORT = 9001;
|
|
11
11
|
const CONNECT_TIMEOUT_MS = 45_000;
|
|
@@ -55,8 +55,7 @@ export function advertisedHost(target) {
|
|
|
55
55
|
* address is restarted.
|
|
56
56
|
*/
|
|
57
57
|
export async function ensureActServer(project, port = DEFAULT_PORT, host = null) {
|
|
58
|
-
const act = resolveAct(project.root);
|
|
59
|
-
if (!act) throw new CliError('ACT not found', 'Install it in the card project: npm i -D @antdigital/cube');
|
|
58
|
+
const act = resolveAct(cardDir(project), project.root);
|
|
60
59
|
if (await isListening(port)) {
|
|
61
60
|
const current = (await serverAddress(port)).host;
|
|
62
61
|
if (!host || current === host) {
|
|
@@ -64,13 +63,13 @@ export async function ensureActServer(project, port = DEFAULT_PORT, host = null)
|
|
|
64
63
|
return act;
|
|
65
64
|
}
|
|
66
65
|
log.detail(`Restarting the ACT server: it advertises ${current}, this device needs ${host}`);
|
|
67
|
-
await
|
|
66
|
+
await runAct(act, ['prepare'], { cwd: project.root, capture: true });
|
|
68
67
|
const freed = Date.now() + 10_000;
|
|
69
68
|
while ((await isListening(port)) && Date.now() < freed) await delay(300);
|
|
70
69
|
}
|
|
71
70
|
mkdirSync(path.join(project.root, '.minicube'), { recursive: true });
|
|
72
71
|
const logFile = path.join(project.root, '.minicube', 'act-server.log');
|
|
73
|
-
spawnDetached(act,
|
|
72
|
+
spawnDetached(process.execPath, [act.script, 'server'], { cwd: project.root, logFd: openSync(logFile, 'a'), env: host ? { HOST: host } : undefined });
|
|
74
73
|
const deadline = Date.now() + 20_000;
|
|
75
74
|
while (Date.now() < deadline) {
|
|
76
75
|
await delay(500);
|
|
@@ -145,6 +144,6 @@ export async function attachDevice(target, env, devices, port = DEFAULT_PORT) {
|
|
|
145
144
|
}
|
|
146
145
|
|
|
147
146
|
export async function deliverCard(project, act) {
|
|
148
|
-
const result = await
|
|
147
|
+
const result = await runAct(act, ['preview', cardDir(project)], { cwd: project.root });
|
|
149
148
|
if (result.code !== 0) throw new CliError(`act preview failed (exit ${result.code})`);
|
|
150
149
|
}
|
package/src/build.js
CHANGED
|
@@ -3,7 +3,7 @@ import { cpSync, existsSync, mkdirSync, readFileSync, rmSync, statSync, writeFil
|
|
|
3
3
|
import os from 'node:os';
|
|
4
4
|
import path from 'node:path';
|
|
5
5
|
import { CliError, log } from './log.js';
|
|
6
|
-
import { run, which } from './proc.js';
|
|
6
|
+
import { run, runSync, which } from './proc.js';
|
|
7
7
|
import { listFiles, zipDirectory } from './zip.js';
|
|
8
8
|
|
|
9
9
|
export const OUT_DIR = '.minicube';
|
|
@@ -39,19 +39,73 @@ function ensureGitIgnored(root) {
|
|
|
39
39
|
const MINI_REQUIRED_OUTPUTS = ['appConfig.json', 'index.html', 'index.js', 'index.worker.js', 'manifest.json'];
|
|
40
40
|
const MINI_SOURCE_EXCLUDES = new Set([OUT_DIR, '.git', '.idea', '.vscode', '.DS_Store']);
|
|
41
41
|
|
|
42
|
+
const IDE_NAME = 'mPaaS小程序开发者工具';
|
|
43
|
+
|
|
44
|
+
/** Where the official mPaaS mini program IDE keeps its compiler (`<app>/builder/`). */
|
|
45
|
+
export function ideBuilderDirs(platform = process.platform, env = process.env, home = os.homedir()) {
|
|
46
|
+
if (platform === 'darwin') {
|
|
47
|
+
return ['/Applications', path.join(home, 'Applications')].map((dir) =>
|
|
48
|
+
path.join(dir, `${IDE_NAME}.app`, 'Contents', 'Resources', 'app', 'builder'));
|
|
49
|
+
}
|
|
50
|
+
if (platform === 'win32') {
|
|
51
|
+
const roots = [
|
|
52
|
+
env.LOCALAPPDATA && path.win32.join(env.LOCALAPPDATA, 'Programs'),
|
|
53
|
+
env.ProgramFiles,
|
|
54
|
+
env['ProgramFiles(x86)'],
|
|
55
|
+
].filter(Boolean);
|
|
56
|
+
return [...roots.map((root) => path.win32.join(root, IDE_NAME)), ...windowsInstallLocations()]
|
|
57
|
+
.map((dir) => path.win32.join(dir, 'resources', 'app', 'builder'));
|
|
58
|
+
}
|
|
59
|
+
return [];
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** Install folders of the IDE recorded by its Windows installer (covers custom locations). */
|
|
63
|
+
function windowsInstallLocations() {
|
|
64
|
+
const found = [];
|
|
65
|
+
for (const hive of ['HKCU', 'HKLM']) {
|
|
66
|
+
try {
|
|
67
|
+
const { code, stdout } = runSync('reg', ['query', `${hive}\\Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall`, '/s', '/f', IDE_NAME, '/d']);
|
|
68
|
+
if (code !== 0) continue;
|
|
69
|
+
for (const key of stdout.split(/\r?\n/).filter((line) => line.startsWith('HKEY_'))) {
|
|
70
|
+
const value = runSync('reg', ['query', key, '/v', 'InstallLocation']).stdout.match(/InstallLocation\s+REG_\w+\s+(.+)/);
|
|
71
|
+
if (value) found.push(value[1].trim().replace(/^"|"$/g, ''));
|
|
72
|
+
}
|
|
73
|
+
} catch {
|
|
74
|
+
// reg.exe unavailable: fall back to the default folders
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return found;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* The mini program compiler: MINICUBE_MINI_BUILDER, then builders.mini in the config, then the
|
|
82
|
+
* copy inside an installed mPaaS mini program IDE. `env` carries the IDE's esbuild, which the
|
|
83
|
+
* compiler (like the IDE) finds through ESBUILD_BINARY_PATH.
|
|
84
|
+
*/
|
|
42
85
|
export function resolveMiniBuilder(globalConfig) {
|
|
43
86
|
const configured = process.env.MINICUBE_MINI_BUILDER ?? globalConfig.data.builders?.mini;
|
|
44
|
-
if (
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
);
|
|
87
|
+
if (configured && !String(configured).includes('<')) {
|
|
88
|
+
const resolved = which(String(configured)) ?? path.resolve(String(configured));
|
|
89
|
+
if (!existsSync(resolved)) throw new CliError(`Mini program builder not found: ${resolved}`);
|
|
90
|
+
return builderAt(resolved);
|
|
49
91
|
}
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
92
|
+
const exe = process.platform === 'win32' ? '.exe' : '';
|
|
93
|
+
for (const dir of ideBuilderDirs()) {
|
|
94
|
+
const file = path.join(dir, `mini-pkg-builder${exe}`);
|
|
95
|
+
if (existsSync(file)) return builderAt(file);
|
|
96
|
+
}
|
|
97
|
+
throw new CliError(
|
|
98
|
+
'No mini program builder found',
|
|
99
|
+
`Install the mPaaS mini program IDE (${IDE_NAME}), or set builders.mini in the minicube-dev config / MINICUBE_MINI_BUILDER to a mini-pkg-builder executable.`,
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function builderAt(file) {
|
|
104
|
+
const esbuild = path.join(path.dirname(file), process.platform === 'win32' ? 'esbuild.exe' : 'esbuild');
|
|
105
|
+
const env = existsSync(esbuild) && !process.env.ESBUILD_BINARY_PATH ? { ESBUILD_BINARY_PATH: esbuild } : {};
|
|
106
|
+
return file.endsWith('.js') || file.endsWith('.mjs')
|
|
107
|
+
? { command: process.execPath, prefix: [file], path: file, env }
|
|
108
|
+
: { command: file, prefix: [], path: file, env };
|
|
55
109
|
}
|
|
56
110
|
|
|
57
111
|
function readMiniProjectConfig(sourceDir) {
|
|
@@ -87,8 +141,9 @@ async function buildMini(project, globalConfig, outRoot) {
|
|
|
87
141
|
'--prod', '--no-minify',
|
|
88
142
|
];
|
|
89
143
|
log.step(`Compiling mini program ${id}`);
|
|
144
|
+
log.detail(`Compiler ${builder.path}`);
|
|
90
145
|
const logFile = path.join(outRoot, 'mini-build.log');
|
|
91
|
-
const result = await run(builder.command, args, { cwd: input, capture: true, env: { FORCE_COLOR: '0', NO_COLOR: '1' } });
|
|
146
|
+
const result = await run(builder.command, args, { cwd: input, capture: true, env: { ...builder.env, FORCE_COLOR: '0', NO_COLOR: '1' } });
|
|
92
147
|
writeFileSync(logFile, `${result.stdout}\n${result.stderr}`);
|
|
93
148
|
rmSync(work, { recursive: true, force: true });
|
|
94
149
|
if (result.code !== 0) {
|
|
@@ -176,10 +231,39 @@ async function buildH5(project, outRoot) {
|
|
|
176
231
|
// ---------------------------------------------------------------------------------------------
|
|
177
232
|
// Cards: cube-pack (inlines local images) when installed, otherwise the project's own `act build`.
|
|
178
233
|
|
|
179
|
-
export
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
234
|
+
export const ACT_MIN_MAJOR = 4;
|
|
235
|
+
const ACT_INSTALL_HINT = 'Install ACT v4 in the card project: npm i -D @antdigital/cube@4';
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* The card project's own ACT v4 CLI script (`@antdigital/cube`), looked up like Node resolves
|
|
239
|
+
* packages: from each start directory up through its parents. A global `act` is never used — it is
|
|
240
|
+
* often ACT v2, whose builds and server the apps' v4 Cube host cannot load.
|
|
241
|
+
*/
|
|
242
|
+
export function resolveAct(...startDirs) {
|
|
243
|
+
for (const start of startDirs) {
|
|
244
|
+
for (let dir = path.resolve(start); ; dir = path.dirname(dir)) {
|
|
245
|
+
const pkgDir = path.join(dir, 'node_modules', '@antdigital', 'cube');
|
|
246
|
+
const pkgFile = path.join(pkgDir, 'package.json');
|
|
247
|
+
if (existsSync(pkgFile)) {
|
|
248
|
+
const pkg = JSON.parse(readFileSync(pkgFile, 'utf8'));
|
|
249
|
+
const major = Number.parseInt(String(pkg.version), 10);
|
|
250
|
+
if (!(major >= ACT_MIN_MAJOR)) {
|
|
251
|
+
throw new CliError(`${pkgDir} is ACT ${pkg.version}; ACT v${ACT_MIN_MAJOR} or newer is required`, ACT_INSTALL_HINT);
|
|
252
|
+
}
|
|
253
|
+
const bin = typeof pkg.bin === 'string' ? pkg.bin : pkg.bin?.act;
|
|
254
|
+
const script = bin && path.join(pkgDir, bin);
|
|
255
|
+
if (!script || !existsSync(script)) throw new CliError(`${pkgDir} has no act CLI`, ACT_INSTALL_HINT);
|
|
256
|
+
return { script, version: pkg.version };
|
|
257
|
+
}
|
|
258
|
+
if (path.dirname(dir) === dir) break;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
throw new CliError(`ACT v${ACT_MIN_MAJOR} (@antdigital/cube) is not installed in the card project`, ACT_INSTALL_HINT);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/** Run the project's ACT CLI with the current Node (also on Windows, where cli.js is not executable). */
|
|
265
|
+
export function runAct(act, args, options) {
|
|
266
|
+
return run(process.execPath, [act.script, ...args], options);
|
|
183
267
|
}
|
|
184
268
|
|
|
185
269
|
export function cardDir(project) {
|
|
@@ -189,16 +273,13 @@ export function cardDir(project) {
|
|
|
189
273
|
async function buildCard(project, outRoot) {
|
|
190
274
|
const dir = cardDir(project);
|
|
191
275
|
if (!existsSync(path.join(dir, 'manifest.json'))) throw new CliError(`${dir} is not a card (no manifest.json)`);
|
|
276
|
+
// Checked even when cube-pack builds: it runs the project's act, so a missing v4 must stop here.
|
|
277
|
+
const act = resolveAct(dir, project.root);
|
|
192
278
|
const cubePack = project.data.card?.useCubePack === false ? null : which('cube-pack');
|
|
193
|
-
log.step(`Building card ${project.data.resourceId}`);
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
} else {
|
|
198
|
-
const act = resolveAct(project.root);
|
|
199
|
-
if (!act) throw new CliError('ACT not found', 'Install it in the card project: npm i -D @antdigital/cube');
|
|
200
|
-
result = await run(act, ['build', dir], { cwd: project.root });
|
|
201
|
-
}
|
|
279
|
+
log.step(`Building card ${project.data.resourceId} (ACT ${act.version})`);
|
|
280
|
+
const result = cubePack
|
|
281
|
+
? await run(cubePack, ['build'], { cwd: project.root })
|
|
282
|
+
: await runAct(act, ['build', dir], { cwd: project.root });
|
|
202
283
|
if (result.code !== 0) throw new CliError(`Card build failed (exit ${result.code})`);
|
|
203
284
|
const zip = findCardZip(dir);
|
|
204
285
|
const file = path.join(outRoot, `${project.data.resourceId}.zip`);
|
package/src/cli.js
CHANGED
|
@@ -385,7 +385,7 @@ const COMMANDS = {
|
|
|
385
385
|
const config = loadGlobalConfig(ctx.flags);
|
|
386
386
|
add('config', config.exists, config.file);
|
|
387
387
|
try {
|
|
388
|
-
add('mini builder', true, resolveMiniBuilder(config).
|
|
388
|
+
add('mini builder', true, resolveMiniBuilder(config).path);
|
|
389
389
|
} catch (e) {
|
|
390
390
|
add('mini builder', false, e.message);
|
|
391
391
|
}
|