@multiplatform.one/cli 6.4.0 → 6.4.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/lib/bin/multiplatformOne.mjs +9 -5
- package/lib/commands/initApp.mjs +27 -0
- package/lib/commands/updateApp.mjs +117 -27
- package/package.json +2 -2
- package/src/bin/multiplatformOne.ts +26 -8
- package/src/commands/initApp.ts +40 -0
- package/src/commands/updateApp.ts +178 -54
- package/templates/app/gitignore +1 -0
- package/templates/universal/apps/__NAME__/vite.config.ts +2 -48
- package/templates/universal/gitignore +2 -0
- package/types/commands/initApp.d.ts +2 -0
- package/types/commands/initApp.d.ts.map +1 -1
- package/types/commands/updateApp.d.ts.map +1 -1
|
@@ -471,7 +471,7 @@ async function getFrappeApps(root) {
|
|
|
471
471
|
const program = new Command();
|
|
472
472
|
program.name("mpo");
|
|
473
473
|
program.version(JSON.parse(fsSync.readFileSync(path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../../package.json"), "utf8"))?.version);
|
|
474
|
-
program.command("init").option("--universal", "scaffold the universal template (web + iOS + Android; default)").option("--web", "scaffold the web-only template (Vite + React)").option("--app", "alias of --web (back-compat)").option("--skip-install", "skip pnpm install after scaffolding").option("--mpo-version <range>", "semver range for @multiplatform.one/* (default: ^<cli version>)").option("-y, --yes", "non-interactive: accept defaults instead of prompting").option("--monorepo", "DEPRECATED: clone/modify the full multiplatform.one monorepo instead of scaffolding a consumer project").option("-c, --checkout <branch>", "branch, tag or commit to checkout (--monorepo only)", "main").option("-a, --apps <apps>", "comma-separated apps to include (--monorepo only)").option("-s, --services <services>", "comma-separated services to include (--monorepo only)").argument("[name]", "the name of the project").description("scaffold a new multiplatform project (universal web + native by default; --web for web-only; semver @multiplatform.one/* deps)").action(async (name, options) => {
|
|
474
|
+
program.command("init").option("--universal", "scaffold the universal template (web + iOS + Android; default)").option("--web", "scaffold the web-only template (Vite + React)").option("--app", "alias of --web (back-compat)").option("--skip-install", "skip pnpm install after scaffolding").option("--skip-git", "skip git init + scaffold commit").option("--mpo-version <range>", "semver range for @multiplatform.one/* (default: ^<cli version>)").option("-y, --yes", "non-interactive: accept defaults instead of prompting").option("--monorepo", "DEPRECATED: clone/modify the full multiplatform.one monorepo instead of scaffolding a consumer project").option("-c, --checkout <branch>", "branch, tag or commit to checkout (--monorepo only)", "main").option("-a, --apps <apps>", "comma-separated apps to include (--monorepo only)").option("-s, --services <services>", "comma-separated services to include (--monorepo only)").argument("[name]", "the name of the project").description("scaffold a new multiplatform project (universal web + native by default; --web for web-only; semver @multiplatform.one/* deps)").action(async (name, options) => {
|
|
475
475
|
if (options.monorepo) {
|
|
476
476
|
console.warn("⚠️ `mpo init --monorepo` clones the whole multiplatform.one monorepo (legacy). New projects should use the default consumer scaffold instead.");
|
|
477
477
|
if (await spawn("git", ["rev-parse", "--is-inside-work-tree"]).then(() => true, () => false)) throw new Error("mpo cannot be initialized inside a git repository");
|
|
@@ -487,6 +487,7 @@ program.command("init").option("--universal", "scaffold the universal template (
|
|
|
487
487
|
if (webOnly && options.universal) throw new Error("Pass either --web/--app or --universal, not both");
|
|
488
488
|
await initApp(name, {
|
|
489
489
|
skipInstall: Boolean(options.skipInstall),
|
|
490
|
+
skipGit: Boolean(options.skipGit),
|
|
490
491
|
version: options.mpoVersion,
|
|
491
492
|
template: webOnly ? "app" : options.universal ? "universal" : void 0,
|
|
492
493
|
yes: Boolean(options.yes)
|
|
@@ -499,11 +500,9 @@ program.command("update").option("-c, --checkout <branch>", "branch, tag or comm
|
|
|
499
500
|
skipInstall: options.skipInstall,
|
|
500
501
|
version: options.mpoVersion
|
|
501
502
|
});
|
|
502
|
-
try {
|
|
503
|
-
await generateVscodeConfig(projectRoot);
|
|
504
|
-
} catch {}
|
|
505
503
|
return;
|
|
506
504
|
}
|
|
505
|
+
if (!await fs.stat(path.resolve(projectRoot, "features/package.json")).then((stat) => stat.isFile(), () => false)) throw new Error(".mpo.json not found — this project predates scaffold provenance. Create it with { template, cliVersion, name } matching how the project was generated (cliVersion = the @multiplatform.one/cli that scaffolded it), then re-run `mpo update`.");
|
|
507
506
|
if (await spawn("git", [
|
|
508
507
|
"diff",
|
|
509
508
|
"--cached",
|
|
@@ -823,7 +822,12 @@ program.command("generate").description("run generate command").action(async ()
|
|
|
823
822
|
cwd: projectRoot
|
|
824
823
|
});
|
|
825
824
|
});
|
|
826
|
-
program.
|
|
825
|
+
program.parseAsync(process.argv).catch((err) => {
|
|
826
|
+
const error = err;
|
|
827
|
+
if (process.env.MPO_DEBUG) console.error(error);
|
|
828
|
+
else console.error(`✖ ${error.message ?? String(err)}`);
|
|
829
|
+
process.exit(1);
|
|
830
|
+
});
|
|
827
831
|
async function waitWithSpinner(services, options = {}) {
|
|
828
832
|
const { interval = 1e3, timeout = 6e5 } = options;
|
|
829
833
|
const waitFunctions = {
|
package/lib/commands/initApp.mjs
CHANGED
|
@@ -151,12 +151,39 @@ async function initApp(nameArg, options = {}) {
|
|
|
151
151
|
name,
|
|
152
152
|
mpoVersion: vars.MPO_VERSION
|
|
153
153
|
});
|
|
154
|
+
let scaffoldCommitted = false;
|
|
155
|
+
if (!options.skipGit) {
|
|
156
|
+
if (!await spawn("git", ["rev-parse", "--is-inside-work-tree"], { cwd: targetDir }).then(() => true, () => false)) {
|
|
157
|
+
const label = template === "universal" ? "universal" : "web-only";
|
|
158
|
+
await spawn("git", ["init", "--quiet"], { cwd: targetDir });
|
|
159
|
+
await spawn("git", ["add", "-A"], { cwd: targetDir });
|
|
160
|
+
scaffoldCommitted = await spawn("git", [
|
|
161
|
+
"commit",
|
|
162
|
+
"--quiet",
|
|
163
|
+
"-m",
|
|
164
|
+
`feat: scaffold ${label} multiplatform.one app (mpo init)`
|
|
165
|
+
], { cwd: targetDir }).then(() => true, () => {
|
|
166
|
+
console.warn("⚠️ git commit failed (missing git identity?) — repo left staged.");
|
|
167
|
+
return false;
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
}
|
|
154
171
|
if (!options.skipInstall) {
|
|
155
172
|
console.log("Installing dependencies...");
|
|
156
173
|
await spawn("pnpm", ["install"], {
|
|
157
174
|
cwd: targetDir,
|
|
158
175
|
stdio: "inherit"
|
|
159
176
|
});
|
|
177
|
+
if (scaffoldCommitted) await spawn("git", [
|
|
178
|
+
"add",
|
|
179
|
+
"--",
|
|
180
|
+
"pnpm-lock.yaml"
|
|
181
|
+
], { cwd: targetDir }).then(() => spawn("git", [
|
|
182
|
+
"commit",
|
|
183
|
+
"--amend",
|
|
184
|
+
"--no-edit",
|
|
185
|
+
"--quiet"
|
|
186
|
+
], { cwd: targetDir })).catch(() => {});
|
|
160
187
|
} else console.log("Skipped pnpm install (--skip-install).");
|
|
161
188
|
console.log(`\n✅ Project created at ${targetDir}`);
|
|
162
189
|
console.log("\nNext steps:");
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { generateVscodeConfig } from "../generateVscode.mjs";
|
|
1
2
|
import { cliVersion, initApp, readProvenance, writeProvenance } from "./initApp.mjs";
|
|
2
3
|
import { join } from "node:path";
|
|
3
4
|
import { existsSync, mkdtempSync, readFileSync, rmSync } from "node:fs";
|
|
@@ -76,8 +77,10 @@ async function scaffoldBaseline(version, name, template, mpoVersion) {
|
|
|
76
77
|
});
|
|
77
78
|
const current = cliVersion();
|
|
78
79
|
const templateFlag = template === "app" ? ["--web"] : ["--universal"];
|
|
79
|
-
|
|
80
|
-
|
|
80
|
+
const [major = 0, minor = 0] = version.split(".").map(Number);
|
|
81
|
+
if (!(major > 6 || major === 6 && minor >= 3)) console.warn(`⚠️ @multiplatform.one/cli@${version} predates the consumer scaffolder (6.3.0); using the current CLI's template as the merge base.`);
|
|
82
|
+
else if (version !== current) {
|
|
83
|
+
for (let attempt = 1; attempt <= 2; attempt++) try {
|
|
81
84
|
await spawn("pnpm", [
|
|
82
85
|
"--package",
|
|
83
86
|
`@multiplatform.one/cli@${version}`,
|
|
@@ -99,9 +102,14 @@ async function scaffoldBaseline(version, name, template, mpoVersion) {
|
|
|
99
102
|
dir,
|
|
100
103
|
cleanup
|
|
101
104
|
};
|
|
102
|
-
console.warn(`⚠️ dlx scaffold for @multiplatform.one/cli@${version} produced no project
|
|
105
|
+
console.warn(`⚠️ dlx scaffold for @multiplatform.one/cli@${version} produced no project`);
|
|
106
|
+
break;
|
|
103
107
|
} catch {
|
|
104
|
-
|
|
108
|
+
rmSync(join(parent, name), {
|
|
109
|
+
recursive: true,
|
|
110
|
+
force: true
|
|
111
|
+
});
|
|
112
|
+
if (attempt === 2) console.warn(`⚠️ could not scaffold baseline with @multiplatform.one/cli@${version} (network?);`);
|
|
105
113
|
}
|
|
106
114
|
console.warn(" falling back to the current CLI's template as the merge base.");
|
|
107
115
|
}
|
|
@@ -110,6 +118,7 @@ async function scaffoldBaseline(version, name, template, mpoVersion) {
|
|
|
110
118
|
try {
|
|
111
119
|
await initApp(name, {
|
|
112
120
|
skipInstall: true,
|
|
121
|
+
skipGit: true,
|
|
113
122
|
yes: true,
|
|
114
123
|
template,
|
|
115
124
|
version: mpoVersion
|
|
@@ -122,6 +131,50 @@ async function scaffoldBaseline(version, name, template, mpoVersion) {
|
|
|
122
131
|
cleanup
|
|
123
132
|
};
|
|
124
133
|
}
|
|
134
|
+
/** Return a tree derived from `tree` where every path matching `pins` is
|
|
135
|
+
* replaced by its content in `headCommit` (removed when absent there).
|
|
136
|
+
* Pure index plumbing — the worktree is untouched. */
|
|
137
|
+
async function pinPathsToHead(projectDir, tree, headCommit, pins) {
|
|
138
|
+
const indexFile = join(mkdtempSync(join(tmpdir(), "mpo-pin-")), "index");
|
|
139
|
+
const env = { GIT_INDEX_FILE: indexFile };
|
|
140
|
+
await git(projectDir, ["read-tree", tree], env);
|
|
141
|
+
for (const pin of pins) {
|
|
142
|
+
const inTree = await gitOut(projectDir, [
|
|
143
|
+
"ls-tree",
|
|
144
|
+
"-r",
|
|
145
|
+
"--name-only",
|
|
146
|
+
tree,
|
|
147
|
+
"--",
|
|
148
|
+
pin
|
|
149
|
+
], env).then((out) => out.split("\n").filter(Boolean)).catch(() => []);
|
|
150
|
+
for (const path of inTree) await git(projectDir, [
|
|
151
|
+
"update-index",
|
|
152
|
+
"--force-remove",
|
|
153
|
+
"--",
|
|
154
|
+
path
|
|
155
|
+
], env).catch(() => {});
|
|
156
|
+
const headEntries = await gitOut(projectDir, [
|
|
157
|
+
"ls-tree",
|
|
158
|
+
"-r",
|
|
159
|
+
headCommit,
|
|
160
|
+
"--",
|
|
161
|
+
pin
|
|
162
|
+
], env).catch(() => "");
|
|
163
|
+
for (const line of headEntries.split("\n").filter(Boolean)) {
|
|
164
|
+
const match = line.match(/^(\d+) \S+ ([0-9a-f]+)\t(.+)$/);
|
|
165
|
+
if (!match) continue;
|
|
166
|
+
await git(projectDir, [
|
|
167
|
+
"update-index",
|
|
168
|
+
"--add",
|
|
169
|
+
"--cacheinfo",
|
|
170
|
+
`${match[1]},${match[2]},${match[3]}`
|
|
171
|
+
], env).catch(() => {});
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
const pinned = await gitOut(projectDir, ["write-tree"], env);
|
|
175
|
+
rmSync(indexFile, { force: true });
|
|
176
|
+
return pinned;
|
|
177
|
+
}
|
|
125
178
|
async function updateApp(options = {}) {
|
|
126
179
|
const projectDir = await gitOut(process.cwd(), ["rev-parse", "--show-toplevel"]).catch(() => {
|
|
127
180
|
throw new Error("mpo update must run inside a git repository");
|
|
@@ -147,12 +200,8 @@ async function updateApp(options = {}) {
|
|
|
147
200
|
const base = await scaffoldBaseline(provenance.cliVersion, provenance.name, provenance.template, provenance.mpoVersion);
|
|
148
201
|
console.log(`Scaffolding update target (cli@${currentVersion})...`);
|
|
149
202
|
const next = await scaffoldBaseline(currentVersion, provenance.name, provenance.template, targetMpoRange);
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
cliVersion: currentVersion,
|
|
153
|
-
name: provenance.name,
|
|
154
|
-
mpoVersion: targetMpoRange
|
|
155
|
-
});
|
|
203
|
+
rmSync(join(base.dir, ".mpo.json"), { force: true });
|
|
204
|
+
rmSync(join(next.dir, ".mpo.json"), { force: true });
|
|
156
205
|
try {
|
|
157
206
|
const head = await gitOut(projectDir, ["rev-parse", "HEAD"]);
|
|
158
207
|
const baseCommit = await commitTreeFromDir(projectDir, base.dir, `mpo scaffold ${provenance.template}@${provenance.cliVersion}`);
|
|
@@ -169,10 +218,11 @@ async function updateApp(options = {}) {
|
|
|
169
218
|
nextCommit
|
|
170
219
|
])).split("\n")[0].trim();
|
|
171
220
|
} catch (err) {
|
|
172
|
-
const lines = (err.stdout ?? "").split("\n")
|
|
173
|
-
if (!lines
|
|
221
|
+
const lines = (err.stdout ?? "").split("\n");
|
|
222
|
+
if (!lines[0]?.trim()) throw err;
|
|
174
223
|
mergedTree = lines[0].trim();
|
|
175
|
-
|
|
224
|
+
const blank = lines.indexOf("", 1);
|
|
225
|
+
conflicts = lines.slice(1, blank === -1 ? void 0 : blank).map((line) => line.trim()).filter(Boolean);
|
|
176
226
|
}
|
|
177
227
|
const finalProvenance = {
|
|
178
228
|
template: provenance.template,
|
|
@@ -180,6 +230,33 @@ async function updateApp(options = {}) {
|
|
|
180
230
|
name: provenance.name,
|
|
181
231
|
mpoVersion: targetMpoRange
|
|
182
232
|
};
|
|
233
|
+
const pins = ["pnpm-lock.yaml"];
|
|
234
|
+
const updateignore = join(projectDir, ".updateignore");
|
|
235
|
+
if (existsSync(updateignore)) for (const line of readFileSync(updateignore, "utf-8").split("\n")) {
|
|
236
|
+
const pattern = line.trim();
|
|
237
|
+
if (pattern && !pattern.startsWith("#")) pins.push(pattern);
|
|
238
|
+
}
|
|
239
|
+
mergedTree = await pinPathsToHead(projectDir, mergedTree, head, pins);
|
|
240
|
+
conflicts = conflicts.filter((file) => !pins.some((pin) => file === pin || file.startsWith(`${pin}/`)));
|
|
241
|
+
const headTree = await gitOut(projectDir, ["rev-parse", `${head}^{tree}`]);
|
|
242
|
+
if (!conflicts.length && mergedTree === headTree) {
|
|
243
|
+
if (provenance.cliVersion !== finalProvenance.cliVersion || provenance.mpoVersion !== finalProvenance.mpoVersion) {
|
|
244
|
+
writeProvenance(projectDir, finalProvenance);
|
|
245
|
+
await git(projectDir, [
|
|
246
|
+
"add",
|
|
247
|
+
"--",
|
|
248
|
+
".mpo.json"
|
|
249
|
+
]);
|
|
250
|
+
await git(projectDir, [
|
|
251
|
+
"commit",
|
|
252
|
+
"--quiet",
|
|
253
|
+
"-m",
|
|
254
|
+
`chore: record mpo template provenance ${currentVersion}`
|
|
255
|
+
]);
|
|
256
|
+
console.log("\n✅ already up to date (provenance refreshed)");
|
|
257
|
+
} else console.log("\n✅ already up to date");
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
183
260
|
if (!conflicts.length) {
|
|
184
261
|
const message = `chore: mpo update ${provenance.cliVersion} → ${currentVersion}`;
|
|
185
262
|
await git(projectDir, [
|
|
@@ -202,10 +279,14 @@ async function updateApp(options = {}) {
|
|
|
202
279
|
"HEAD"
|
|
203
280
|
]);
|
|
204
281
|
writeProvenance(projectDir, finalProvenance);
|
|
282
|
+
try {
|
|
283
|
+
await generateVscodeConfig(projectDir);
|
|
284
|
+
} catch {}
|
|
205
285
|
await git(projectDir, [
|
|
206
286
|
"add",
|
|
207
287
|
"--",
|
|
208
|
-
".mpo.json"
|
|
288
|
+
".mpo.json",
|
|
289
|
+
".vscode"
|
|
209
290
|
]);
|
|
210
291
|
await git(projectDir, [
|
|
211
292
|
"commit",
|
|
@@ -231,24 +312,33 @@ async function updateApp(options = {}) {
|
|
|
231
312
|
for (const file of conflicts) console.log(` ${file}`);
|
|
232
313
|
console.log(`\n git add -A && git commit -m "chore: mpo update ${provenance.cliVersion} → ${currentVersion}"`);
|
|
233
314
|
}
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
if (existsSync(updateignore)) for (const line of readFileSync(updateignore, "utf-8").split("\n")) {
|
|
237
|
-
const pattern = line.trim();
|
|
238
|
-
if (pattern && !pattern.startsWith("#")) pins.push(pattern);
|
|
239
|
-
}
|
|
240
|
-
for (const pin of pins) await git(projectDir, [
|
|
241
|
-
"checkout",
|
|
242
|
-
head,
|
|
243
|
-
"--",
|
|
244
|
-
pin
|
|
245
|
-
]).catch(() => {});
|
|
246
|
-
if (!options.skipInstall) {
|
|
315
|
+
if (conflicts.length && !options.skipInstall) console.log("\nSkipping pnpm install until conflicts are resolved (then run: pnpm install).");
|
|
316
|
+
else if (!options.skipInstall) {
|
|
247
317
|
console.log("\nInstalling dependencies...");
|
|
248
318
|
await spawn("pnpm", ["install"], {
|
|
249
319
|
cwd: projectDir,
|
|
250
320
|
stdio: "inherit"
|
|
251
321
|
});
|
|
322
|
+
if (!conflicts.length) {
|
|
323
|
+
if (await git(projectDir, [
|
|
324
|
+
"diff",
|
|
325
|
+
"--quiet",
|
|
326
|
+
"--",
|
|
327
|
+
"pnpm-lock.yaml"
|
|
328
|
+
]).then(() => false, () => true)) {
|
|
329
|
+
await git(projectDir, [
|
|
330
|
+
"add",
|
|
331
|
+
"--",
|
|
332
|
+
"pnpm-lock.yaml"
|
|
333
|
+
]);
|
|
334
|
+
await git(projectDir, [
|
|
335
|
+
"commit",
|
|
336
|
+
"--amend",
|
|
337
|
+
"--no-edit",
|
|
338
|
+
"--quiet"
|
|
339
|
+
]);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
252
342
|
}
|
|
253
343
|
} finally {
|
|
254
344
|
base.cleanup();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@multiplatform.one/cli",
|
|
3
|
-
"version": "6.4.
|
|
3
|
+
"version": "6.4.1",
|
|
4
4
|
"description": "multiplatform.one cli — mpo init / create-multiplatform-app",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"create-multiplatform-app",
|
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
"nano-spawn": "^2.1.0",
|
|
62
62
|
"yaml": "^2.8.3",
|
|
63
63
|
"yocto-spinner": "^1.1.0",
|
|
64
|
-
"@multiplatform.one/utils": "6.4.
|
|
64
|
+
"@multiplatform.one/utils": "6.4.1"
|
|
65
65
|
},
|
|
66
66
|
"devDependencies": {
|
|
67
67
|
"@types/inquirer": "^9.0.9",
|
|
@@ -222,6 +222,7 @@ program
|
|
|
222
222
|
.option("--web", "scaffold the web-only template (Vite + React)")
|
|
223
223
|
.option("--app", "alias of --web (back-compat)")
|
|
224
224
|
.option("--skip-install", "skip pnpm install after scaffolding")
|
|
225
|
+
.option("--skip-git", "skip git init + scaffold commit")
|
|
225
226
|
.option(
|
|
226
227
|
"--mpo-version <range>",
|
|
227
228
|
"semver range for @multiplatform.one/* (default: ^<cli version>)",
|
|
@@ -270,6 +271,7 @@ program
|
|
|
270
271
|
}
|
|
271
272
|
await initApp(name, {
|
|
272
273
|
skipInstall: Boolean(options.skipInstall),
|
|
274
|
+
skipGit: Boolean(options.skipGit),
|
|
273
275
|
version: options.mpoVersion,
|
|
274
276
|
template: webOnly ? "app" : options.universal ? "universal" : undefined,
|
|
275
277
|
yes: Boolean(options.yes),
|
|
@@ -306,17 +308,26 @@ program
|
|
|
306
308
|
throw new Error("mpo cannot be updated outside of a git repository");
|
|
307
309
|
}
|
|
308
310
|
// Scaffolded consumer projects carry .mpo.json provenance — use the
|
|
309
|
-
// copier-style three-way template update. Monorepo forks
|
|
310
|
-
// the legacy upstream-
|
|
311
|
+
// copier-style three-way template update. Monorepo forks (identified by
|
|
312
|
+
// the legacy features/package.json marker) fall through to the upstream-
|
|
313
|
+
// merge flow below; anything else gets the provenance guidance instead
|
|
314
|
+
// of the fork flow's unrelated errors.
|
|
311
315
|
if (readProvenance(projectRoot)) {
|
|
312
316
|
await updateApp({ skipInstall: options.skipInstall, version: options.mpoVersion });
|
|
313
|
-
try {
|
|
314
|
-
await generateVscodeConfig(projectRoot);
|
|
315
|
-
} catch {
|
|
316
|
-
// best-effort
|
|
317
|
-
}
|
|
318
317
|
return;
|
|
319
318
|
}
|
|
319
|
+
const legacyFork = await fs.stat(path.resolve(projectRoot, "features/package.json")).then(
|
|
320
|
+
(stat) => stat.isFile(),
|
|
321
|
+
() => false,
|
|
322
|
+
);
|
|
323
|
+
if (!legacyFork) {
|
|
324
|
+
throw new Error(
|
|
325
|
+
".mpo.json not found — this project predates scaffold provenance. " +
|
|
326
|
+
"Create it with { template, cliVersion, name } matching how the project " +
|
|
327
|
+
"was generated (cliVersion = the @multiplatform.one/cli that scaffolded it), " +
|
|
328
|
+
"then re-run `mpo update`.",
|
|
329
|
+
);
|
|
330
|
+
}
|
|
320
331
|
// Require clean working tree (no staged or unstaged changes)
|
|
321
332
|
if (
|
|
322
333
|
await spawn("git", ["diff", "--cached", "--quiet"]).then(
|
|
@@ -806,7 +817,14 @@ program
|
|
|
806
817
|
});
|
|
807
818
|
});
|
|
808
819
|
|
|
809
|
-
|
|
820
|
+
// One-line errors instead of raw stack traces (matches the
|
|
821
|
+
// create-multiplatform-app bin). Stacks stay available via MPO_DEBUG=1.
|
|
822
|
+
program.parseAsync(process.argv).catch((err: unknown) => {
|
|
823
|
+
const error = err as Error;
|
|
824
|
+
if (process.env.MPO_DEBUG) console.error(error);
|
|
825
|
+
else console.error(`✖ ${error.message ?? String(err)}`);
|
|
826
|
+
process.exit(1);
|
|
827
|
+
});
|
|
810
828
|
|
|
811
829
|
async function waitWithSpinner(
|
|
812
830
|
services: string[],
|
package/src/commands/initApp.ts
CHANGED
|
@@ -23,6 +23,8 @@ export type InitAppTemplate = "universal" | "app";
|
|
|
23
23
|
export interface InitAppOptions {
|
|
24
24
|
/** Skip pnpm install (useful for CI / dry scaffolding). */
|
|
25
25
|
skipInstall?: boolean;
|
|
26
|
+
/** Skip git init + scaffold commit (mpo update baselines, nested dirs). */
|
|
27
|
+
skipGit?: boolean;
|
|
26
28
|
/** Pin @multiplatform.one/* to this semver range. Default: ^<cli version>. */
|
|
27
29
|
version?: string;
|
|
28
30
|
/** Project template. When omitted: prompt (interactive) or "universal". */
|
|
@@ -250,12 +252,50 @@ export async function initApp(
|
|
|
250
252
|
mpoVersion: vars.MPO_VERSION,
|
|
251
253
|
});
|
|
252
254
|
|
|
255
|
+
// Initialize git with a scaffold commit (unless already inside a repo —
|
|
256
|
+
// e.g. scaffolding into an existing project dir). `mpo update` needs the
|
|
257
|
+
// scaffold as a committed baseline to merge on top of.
|
|
258
|
+
let scaffoldCommitted = false;
|
|
259
|
+
if (!options.skipGit) {
|
|
260
|
+
const insideRepo = await spawn("git", ["rev-parse", "--is-inside-work-tree"], {
|
|
261
|
+
cwd: targetDir,
|
|
262
|
+
}).then(
|
|
263
|
+
() => true,
|
|
264
|
+
() => false,
|
|
265
|
+
);
|
|
266
|
+
if (!insideRepo) {
|
|
267
|
+
const label = template === "universal" ? "universal" : "web-only";
|
|
268
|
+
await spawn("git", ["init", "--quiet"], { cwd: targetDir });
|
|
269
|
+
await spawn("git", ["add", "-A"], { cwd: targetDir });
|
|
270
|
+
scaffoldCommitted = await spawn(
|
|
271
|
+
"git",
|
|
272
|
+
["commit", "--quiet", "-m", `feat: scaffold ${label} multiplatform.one app (mpo init)`],
|
|
273
|
+
{ cwd: targetDir },
|
|
274
|
+
).then(
|
|
275
|
+
() => true,
|
|
276
|
+
() => {
|
|
277
|
+
console.warn("⚠️ git commit failed (missing git identity?) — repo left staged.");
|
|
278
|
+
return false;
|
|
279
|
+
},
|
|
280
|
+
);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
253
284
|
if (!options.skipInstall) {
|
|
254
285
|
console.log("Installing dependencies...");
|
|
255
286
|
await spawn("pnpm", ["install"], {
|
|
256
287
|
cwd: targetDir,
|
|
257
288
|
stdio: "inherit",
|
|
258
289
|
});
|
|
290
|
+
// The lockfile belongs to the scaffold — fold it into the scaffold
|
|
291
|
+
// commit so a fresh init is immediately clean.
|
|
292
|
+
if (scaffoldCommitted) {
|
|
293
|
+
await spawn("git", ["add", "--", "pnpm-lock.yaml"], { cwd: targetDir })
|
|
294
|
+
.then(() =>
|
|
295
|
+
spawn("git", ["commit", "--amend", "--no-edit", "--quiet"], { cwd: targetDir }),
|
|
296
|
+
)
|
|
297
|
+
.catch(() => {});
|
|
298
|
+
}
|
|
259
299
|
} else {
|
|
260
300
|
console.log("Skipped pnpm install (--skip-install).");
|
|
261
301
|
}
|
|
@@ -19,6 +19,7 @@ import { existsSync, mkdtempSync, readFileSync, rmSync } from "node:fs";
|
|
|
19
19
|
import { tmpdir } from "node:os";
|
|
20
20
|
import { join } from "node:path";
|
|
21
21
|
import spawn from "nano-spawn";
|
|
22
|
+
import { generateVscodeConfig } from "../generateVscode";
|
|
22
23
|
import { cliVersion, initApp, readProvenance, writeProvenance } from "./initApp";
|
|
23
24
|
|
|
24
25
|
export interface UpdateAppOptions {
|
|
@@ -73,32 +74,51 @@ async function scaffoldBaseline(
|
|
|
73
74
|
const cleanup = () => rmSync(parent, { recursive: true, force: true });
|
|
74
75
|
const current = cliVersion();
|
|
75
76
|
const templateFlag = template === "app" ? ["--web"] : ["--universal"];
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
77
|
+
// The consumer scaffolder (initApp + templates) shipped in 6.3.0 —
|
|
78
|
+
// earlier CLIs only had the monorepo-clone init and cannot regenerate a
|
|
79
|
+
// scaffold baseline.
|
|
80
|
+
const [major = 0, minor = 0] = version.split(".").map(Number);
|
|
81
|
+
const scaffoldCapable = major > 6 || (major === 6 && minor >= 3);
|
|
82
|
+
if (!scaffoldCapable) {
|
|
83
|
+
console.warn(
|
|
84
|
+
`⚠️ @multiplatform.one/cli@${version} predates the consumer scaffolder (6.3.0); ` +
|
|
85
|
+
"using the current CLI's template as the merge base.",
|
|
86
|
+
);
|
|
87
|
+
} else if (version !== current) {
|
|
88
|
+
// Older published CLIs reject flags they don't know (--skip-git), so
|
|
89
|
+
// only the stable flag set is passed; a scaffold-side git repo is
|
|
90
|
+
// harmless (index operations never descend into nested .git dirs).
|
|
91
|
+
for (let attempt = 1; attempt <= 2; attempt++) {
|
|
92
|
+
try {
|
|
93
|
+
await spawn(
|
|
94
|
+
"pnpm",
|
|
95
|
+
[
|
|
96
|
+
"--package",
|
|
97
|
+
`@multiplatform.one/cli@${version}`,
|
|
98
|
+
"dlx",
|
|
99
|
+
"mpo",
|
|
100
|
+
"init",
|
|
101
|
+
name,
|
|
102
|
+
"--yes",
|
|
103
|
+
"--skip-install",
|
|
104
|
+
"--mpo-version",
|
|
105
|
+
mpoVersion,
|
|
106
|
+
...templateFlag,
|
|
107
|
+
],
|
|
108
|
+
{ cwd: parent, stdio: "inherit" },
|
|
109
|
+
);
|
|
110
|
+
const dir = join(parent, name);
|
|
111
|
+
if (existsSync(join(dir, "package.json"))) return { dir, cleanup };
|
|
112
|
+
console.warn(`⚠️ dlx scaffold for @multiplatform.one/cli@${version} produced no project`);
|
|
113
|
+
break;
|
|
114
|
+
} catch {
|
|
115
|
+
rmSync(join(parent, name), { recursive: true, force: true });
|
|
116
|
+
if (attempt === 2) {
|
|
117
|
+
console.warn(
|
|
118
|
+
`⚠️ could not scaffold baseline with @multiplatform.one/cli@${version} (network?);`,
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
102
122
|
}
|
|
103
123
|
console.warn(" falling back to the current CLI's template as the merge base.");
|
|
104
124
|
}
|
|
@@ -107,6 +127,7 @@ async function scaffoldBaseline(
|
|
|
107
127
|
try {
|
|
108
128
|
await initApp(name, {
|
|
109
129
|
skipInstall: true,
|
|
130
|
+
skipGit: true,
|
|
110
131
|
yes: true,
|
|
111
132
|
template,
|
|
112
133
|
version: mpoVersion,
|
|
@@ -117,6 +138,48 @@ async function scaffoldBaseline(
|
|
|
117
138
|
return { dir: join(parent, name), cleanup };
|
|
118
139
|
}
|
|
119
140
|
|
|
141
|
+
/** Return a tree derived from `tree` where every path matching `pins` is
|
|
142
|
+
* replaced by its content in `headCommit` (removed when absent there).
|
|
143
|
+
* Pure index plumbing — the worktree is untouched. */
|
|
144
|
+
async function pinPathsToHead(
|
|
145
|
+
projectDir: string,
|
|
146
|
+
tree: string,
|
|
147
|
+
headCommit: string,
|
|
148
|
+
pins: string[],
|
|
149
|
+
): Promise<string> {
|
|
150
|
+
const indexFile = join(mkdtempSync(join(tmpdir(), "mpo-pin-")), "index");
|
|
151
|
+
const env = { GIT_INDEX_FILE: indexFile };
|
|
152
|
+
await git(projectDir, ["read-tree", tree], env);
|
|
153
|
+
for (const pin of pins) {
|
|
154
|
+
// Paths under the pin in the merged tree (to drop when HEAD lacks them).
|
|
155
|
+
const inTree = await gitOut(projectDir, ["ls-tree", "-r", "--name-only", tree, "--", pin], env)
|
|
156
|
+
.then((out) => out.split("\n").filter(Boolean))
|
|
157
|
+
.catch(() => [] as string[]);
|
|
158
|
+
for (const path of inTree) {
|
|
159
|
+
await git(projectDir, ["update-index", "--force-remove", "--", path], env).catch(() => {});
|
|
160
|
+
}
|
|
161
|
+
// HEAD's entries for the pin (restored verbatim).
|
|
162
|
+
const headEntries = await gitOut(
|
|
163
|
+
projectDir,
|
|
164
|
+
["ls-tree", "-r", headCommit, "--", pin],
|
|
165
|
+
env,
|
|
166
|
+
).catch(() => "");
|
|
167
|
+
for (const line of headEntries.split("\n").filter(Boolean)) {
|
|
168
|
+
// "<mode> blob <oid>\t<path>"
|
|
169
|
+
const match = line.match(/^(\d+) \S+ ([0-9a-f]+)\t(.+)$/);
|
|
170
|
+
if (!match) continue;
|
|
171
|
+
await git(
|
|
172
|
+
projectDir,
|
|
173
|
+
["update-index", "--add", "--cacheinfo", `${match[1]},${match[2]},${match[3]}`],
|
|
174
|
+
env,
|
|
175
|
+
).catch(() => {});
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
const pinned = await gitOut(projectDir, ["write-tree"], env);
|
|
179
|
+
rmSync(indexFile, { force: true });
|
|
180
|
+
return pinned;
|
|
181
|
+
}
|
|
182
|
+
|
|
120
183
|
export async function updateApp(options: UpdateAppOptions = {}): Promise<void> {
|
|
121
184
|
const projectDir = await gitOut(process.cwd(), ["rev-parse", "--show-toplevel"]).catch(() => {
|
|
122
185
|
throw new Error("mpo update must run inside a git repository");
|
|
@@ -168,12 +231,13 @@ export async function updateApp(options: UpdateAppOptions = {}): Promise<void> {
|
|
|
168
231
|
provenance.template,
|
|
169
232
|
targetMpoRange,
|
|
170
233
|
);
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
});
|
|
234
|
+
|
|
235
|
+
// .mpo.json is TOOL-owned, never merged: pre-provenance CLIs (≤6.3.0)
|
|
236
|
+
// didn't write it, so leaving it in the scaffolds guarantees an add/add
|
|
237
|
+
// phantom conflict on every honest old-baseline update. It is stripped
|
|
238
|
+
// from both synthetic trees and written authoritatively after the merge.
|
|
239
|
+
rmSync(join(base.dir, ".mpo.json"), { force: true });
|
|
240
|
+
rmSync(join(next.dir, ".mpo.json"), { force: true });
|
|
177
241
|
|
|
178
242
|
try {
|
|
179
243
|
// 3. Three-way merge: base → HEAD (ours) vs base → next (theirs).
|
|
@@ -203,12 +267,18 @@ export async function updateApp(options: UpdateAppOptions = {}): Promise<void> {
|
|
|
203
267
|
]);
|
|
204
268
|
mergedTree = out.split("\n")[0]!.trim();
|
|
205
269
|
} catch (err) {
|
|
206
|
-
// Exit code 1 = conflicts
|
|
270
|
+
// Exit code 1 = conflicts. Output format: <tree>\n<conflicted file
|
|
271
|
+
// names, one per line>\n\n<informational messages> — only the names
|
|
272
|
+
// section (up to the blank line) lists conflicted paths.
|
|
207
273
|
const stdout = (err as { stdout?: string }).stdout ?? "";
|
|
208
|
-
const lines = stdout.split("\n")
|
|
209
|
-
if (!lines
|
|
210
|
-
mergedTree = lines[0]
|
|
211
|
-
|
|
274
|
+
const lines = stdout.split("\n");
|
|
275
|
+
if (!lines[0]?.trim()) throw err;
|
|
276
|
+
mergedTree = lines[0].trim();
|
|
277
|
+
const blank = lines.indexOf("", 1);
|
|
278
|
+
conflicts = lines
|
|
279
|
+
.slice(1, blank === -1 ? undefined : blank)
|
|
280
|
+
.map((line) => line.trim())
|
|
281
|
+
.filter(Boolean);
|
|
212
282
|
}
|
|
213
283
|
|
|
214
284
|
// The merged .mpo.json can interleave ours/theirs lines (add/add
|
|
@@ -221,6 +291,47 @@ export async function updateApp(options: UpdateAppOptions = {}): Promise<void> {
|
|
|
221
291
|
mpoVersion: targetMpoRange,
|
|
222
292
|
};
|
|
223
293
|
|
|
294
|
+
// .updateignore pathspecs (+ pnpm-lock.yaml) pin to the pre-update HEAD.
|
|
295
|
+
// Pins are applied to the MERGED TREE before committing, so a clean
|
|
296
|
+
// update never leaves the tree dirty.
|
|
297
|
+
const pins = ["pnpm-lock.yaml"];
|
|
298
|
+
const updateignore = join(projectDir, ".updateignore");
|
|
299
|
+
if (existsSync(updateignore)) {
|
|
300
|
+
for (const line of readFileSync(updateignore, "utf-8").split("\n")) {
|
|
301
|
+
const pattern = line.trim();
|
|
302
|
+
if (pattern && !pattern.startsWith("#")) pins.push(pattern);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
mergedTree = await pinPathsToHead(projectDir, mergedTree, head, pins);
|
|
306
|
+
// Pinning resolves any conflict inside a pinned path (the pre-update
|
|
307
|
+
// HEAD content wins by definition).
|
|
308
|
+
conflicts = conflicts.filter(
|
|
309
|
+
(file) => !pins.some((pin) => file === pin || file.startsWith(`${pin}/`)),
|
|
310
|
+
);
|
|
311
|
+
|
|
312
|
+
// No-op detection: identical trees mean the project already carries the
|
|
313
|
+
// current template — don't stack empty merge commits.
|
|
314
|
+
const headTree = await gitOut(projectDir, ["rev-parse", `${head}^{tree}`]);
|
|
315
|
+
if (!conflicts.length && mergedTree === headTree) {
|
|
316
|
+
if (
|
|
317
|
+
provenance.cliVersion !== finalProvenance.cliVersion ||
|
|
318
|
+
provenance.mpoVersion !== finalProvenance.mpoVersion
|
|
319
|
+
) {
|
|
320
|
+
writeProvenance(projectDir, finalProvenance);
|
|
321
|
+
await git(projectDir, ["add", "--", ".mpo.json"]);
|
|
322
|
+
await git(projectDir, [
|
|
323
|
+
"commit",
|
|
324
|
+
"--quiet",
|
|
325
|
+
"-m",
|
|
326
|
+
`chore: record mpo template provenance ${currentVersion}`,
|
|
327
|
+
]);
|
|
328
|
+
console.log("\n✅ already up to date (provenance refreshed)");
|
|
329
|
+
} else {
|
|
330
|
+
console.log("\n✅ already up to date");
|
|
331
|
+
}
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
|
|
224
335
|
if (!conflicts.length) {
|
|
225
336
|
const message = `chore: mpo update ${provenance.cliVersion} → ${currentVersion}`;
|
|
226
337
|
const mergeCommit = await gitOut(projectDir, [
|
|
@@ -235,14 +346,21 @@ export async function updateApp(options: UpdateAppOptions = {}): Promise<void> {
|
|
|
235
346
|
]);
|
|
236
347
|
await git(projectDir, ["update-ref", "HEAD", mergeCommit]);
|
|
237
348
|
await git(projectDir, ["reset", "--hard", "HEAD"]);
|
|
349
|
+
// Tool-owned outputs belong to the update commit: provenance + the
|
|
350
|
+
// regenerated .vscode config.
|
|
238
351
|
writeProvenance(projectDir, finalProvenance);
|
|
239
|
-
|
|
352
|
+
try {
|
|
353
|
+
await generateVscodeConfig(projectDir);
|
|
354
|
+
} catch {
|
|
355
|
+
// best-effort (older layouts)
|
|
356
|
+
}
|
|
357
|
+
await git(projectDir, ["add", "--", ".mpo.json", ".vscode"]);
|
|
240
358
|
await git(projectDir, ["commit", "--amend", "--no-edit", "--quiet"]);
|
|
241
359
|
const amended = await gitOut(projectDir, ["rev-parse", "--short", "HEAD"]);
|
|
242
360
|
console.log(`\n✅ merged cleanly → ${amended} ("${message}")`);
|
|
243
361
|
} else {
|
|
244
362
|
// Land the merged tree (with conflict markers) in index + worktree for
|
|
245
|
-
// manual resolution.
|
|
363
|
+
// manual resolution; pins are already part of the tree.
|
|
246
364
|
await git(projectDir, ["read-tree", "-u", "--reset", mergedTree]);
|
|
247
365
|
writeProvenance(projectDir, finalProvenance);
|
|
248
366
|
console.log("\n⚠️ merge conflicts — resolve the markers, then commit:");
|
|
@@ -252,23 +370,29 @@ export async function updateApp(options: UpdateAppOptions = {}): Promise<void> {
|
|
|
252
370
|
);
|
|
253
371
|
}
|
|
254
372
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
const updateignore = join(projectDir, ".updateignore");
|
|
259
|
-
if (existsSync(updateignore)) {
|
|
260
|
-
for (const line of readFileSync(updateignore, "utf-8").split("\n")) {
|
|
261
|
-
const pattern = line.trim();
|
|
262
|
-
if (pattern && !pattern.startsWith("#")) pins.push(pattern);
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
for (const pin of pins) {
|
|
266
|
-
await git(projectDir, ["checkout", head, "--", pin]).catch(() => {});
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
if (!options.skipInstall) {
|
|
373
|
+
if (conflicts.length && !options.skipInstall) {
|
|
374
|
+
console.log("\nSkipping pnpm install until conflicts are resolved (then run: pnpm install).");
|
|
375
|
+
} else if (!options.skipInstall) {
|
|
270
376
|
console.log("\nInstalling dependencies...");
|
|
271
377
|
await spawn("pnpm", ["install"], { cwd: projectDir, stdio: "inherit" });
|
|
378
|
+
// The merge changed dependency ranges, so the regenerated lockfile
|
|
379
|
+
// belongs with the update: amend it into the clean-merge commit
|
|
380
|
+
// (conflict resolutions commit manually and pick it up there).
|
|
381
|
+
if (!conflicts.length) {
|
|
382
|
+
const lockDirty = await git(projectDir, [
|
|
383
|
+
"diff",
|
|
384
|
+
"--quiet",
|
|
385
|
+
"--",
|
|
386
|
+
"pnpm-lock.yaml",
|
|
387
|
+
]).then(
|
|
388
|
+
() => false,
|
|
389
|
+
() => true,
|
|
390
|
+
);
|
|
391
|
+
if (lockDirty) {
|
|
392
|
+
await git(projectDir, ["add", "--", "pnpm-lock.yaml"]);
|
|
393
|
+
await git(projectDir, ["commit", "--amend", "--no-edit", "--quiet"]);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
272
396
|
}
|
|
273
397
|
} finally {
|
|
274
398
|
base.cleanup();
|
package/templates/app/gitignore
CHANGED
|
@@ -1,53 +1,8 @@
|
|
|
1
|
-
import { existsSync } from "node:fs";
|
|
2
|
-
import { createRequire } from "node:module";
|
|
3
1
|
import { createViteConfig } from "@multiplatform.one/config/vite";
|
|
4
2
|
import { one } from "one/vite";
|
|
5
|
-
import
|
|
6
|
-
import { public as publicConfigKeys } from "../../packages/config/config.json";
|
|
3
|
+
import configJson from "../../packages/config/config.json" with { type: "json" };
|
|
7
4
|
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Some @multiplatform.one packages at 6.1.0 publish web export targets that
|
|
12
|
-
* don't exist in their tarballs (exports say dist/esm/…/index.js; the build
|
|
13
|
-
* emitted index.mjs), which breaks resolution for anything importing them —
|
|
14
|
-
* e.g. forms → router and frappe-ui → frappe. Resolve those bare imports to
|
|
15
|
-
* the real web builds. Native is unaffected (vxrn's native resolver uses the
|
|
16
|
-
* packages' valid react-native export condition). Remove this plugin once
|
|
17
|
-
* fixed packages are published.
|
|
18
|
-
*/
|
|
19
|
-
const BROKEN_PUBLISHED_ENTRIES: Record<string, { pkg: string; entry: string }> = {
|
|
20
|
-
"@multiplatform.one/router": {
|
|
21
|
-
pkg: "@multiplatform.one/router",
|
|
22
|
-
entry: "dist/esm/index.mjs",
|
|
23
|
-
},
|
|
24
|
-
"@multiplatform.one/frappe": {
|
|
25
|
-
pkg: "@multiplatform.one/frappe",
|
|
26
|
-
entry: "dist/esm/index.mjs",
|
|
27
|
-
},
|
|
28
|
-
"@multiplatform.one/frappe/devtools": {
|
|
29
|
-
pkg: "@multiplatform.one/frappe",
|
|
30
|
-
entry: "dist/esm/devtools/index.mjs",
|
|
31
|
-
},
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
function fixPublishedEsmEntries(): Plugin {
|
|
35
|
-
return {
|
|
36
|
-
name: "fix-multiplatform-published-esm-entries",
|
|
37
|
-
enforce: "pre",
|
|
38
|
-
resolveId(id) {
|
|
39
|
-
const broken = BROKEN_PUBLISHED_ENTRIES[id];
|
|
40
|
-
if (!broken) return null;
|
|
41
|
-
try {
|
|
42
|
-
const pkgJson = require.resolve(`${broken.pkg}/package.json`);
|
|
43
|
-
const entry = pkgJson.replace(/package\.json$/, broken.entry);
|
|
44
|
-
return existsSync(entry) ? entry : null;
|
|
45
|
-
} catch {
|
|
46
|
-
return null;
|
|
47
|
-
}
|
|
48
|
-
},
|
|
49
|
-
};
|
|
50
|
-
}
|
|
5
|
+
const publicConfigKeys = configJson.public;
|
|
51
6
|
|
|
52
7
|
export default createViteConfig({
|
|
53
8
|
publicConfigKeys,
|
|
@@ -61,6 +16,5 @@ export default createViteConfig({
|
|
|
61
16
|
react: { compiler: true },
|
|
62
17
|
native: { key: "__NAME_PASCAL__" },
|
|
63
18
|
}),
|
|
64
|
-
fixPublishedEsmEntries(),
|
|
65
19
|
],
|
|
66
20
|
});
|
|
@@ -9,6 +9,8 @@ export type InitAppTemplate = "universal" | "app";
|
|
|
9
9
|
export interface InitAppOptions {
|
|
10
10
|
/** Skip pnpm install (useful for CI / dry scaffolding). */
|
|
11
11
|
skipInstall?: boolean;
|
|
12
|
+
/** Skip git init + scaffold commit (mpo update baselines, nested dirs). */
|
|
13
|
+
skipGit?: boolean;
|
|
12
14
|
/** Pin @multiplatform.one/* to this semver range. Default: ^<cli version>. */
|
|
13
15
|
version?: string;
|
|
14
16
|
/** Project template. When omitted: prompt (interactive) or "universal". */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"initApp.d.ts","sourceRoot":"","sources":["../../src/commands/initApp.ts"],"names":[],"mappings":"AAaA;;;;;;GAMG;AACH,MAAM,MAAM,eAAe,GAAG,WAAW,GAAG,KAAK,CAAC;AAElD,MAAM,WAAW,cAAc;IAC7B,2DAA2D;IAC3D,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,8EAA8E;IAC9E,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,2EAA2E;IAC3E,QAAQ,CAAC,EAAE,eAAe,CAAC;IAC3B,mEAAmE;IACnE,GAAG,CAAC,EAAE,OAAO,CAAC;CACf;AA0CD,wBAAgB,UAAU,IAAI,MAAM,GAAG,SAAS,CAQ/C;AAED;;;;;GAKG;AACH,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,eAAe,CAAC;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,eAAO,MAAM,eAAe,cAAc,CAAC;AAE3C,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,aAAa,GAAG,IAAI,CAElF;AAED,wBAAgB,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS,CAe5E;AAuED;;;;GAIG;AACH,wBAAsB,OAAO,CAC3B,OAAO,EAAE,MAAM,GAAG,SAAS,EAC3B,OAAO,GAAE,cAAmB,GAC3B,OAAO,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"initApp.d.ts","sourceRoot":"","sources":["../../src/commands/initApp.ts"],"names":[],"mappings":"AAaA;;;;;;GAMG;AACH,MAAM,MAAM,eAAe,GAAG,WAAW,GAAG,KAAK,CAAC;AAElD,MAAM,WAAW,cAAc;IAC7B,2DAA2D;IAC3D,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,2EAA2E;IAC3E,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,8EAA8E;IAC9E,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,2EAA2E;IAC3E,QAAQ,CAAC,EAAE,eAAe,CAAC;IAC3B,mEAAmE;IACnE,GAAG,CAAC,EAAE,OAAO,CAAC;CACf;AA0CD,wBAAgB,UAAU,IAAI,MAAM,GAAG,SAAS,CAQ/C;AAED;;;;;GAKG;AACH,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,eAAe,CAAC;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,eAAO,MAAM,eAAe,cAAc,CAAC;AAE3C,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,aAAa,GAAG,IAAI,CAElF;AAED,wBAAgB,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS,CAe5E;AAuED;;;;GAIG;AACH,wBAAsB,OAAO,CAC3B,OAAO,EAAE,MAAM,GAAG,SAAS,EAC3B,OAAO,GAAE,cAAmB,GAC3B,OAAO,CAAC,IAAI,CAAC,CAmHf"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"updateApp.d.ts","sourceRoot":"","sources":["../../src/commands/updateApp.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;
|
|
1
|
+
{"version":3,"file":"updateApp.d.ts","sourceRoot":"","sources":["../../src/commands/updateApp.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AASH,MAAM,WAAW,gBAAgB;IAC/B,yCAAyC;IACzC,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,uEAAuE;IACvE,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAyJD,wBAAsB,SAAS,CAAC,OAAO,GAAE,gBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC,CA0N7E"}
|