@weapp-tailwindcss/env 0.1.0 → 0.2.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/dist/index.cjs CHANGED
@@ -62,22 +62,74 @@ function isRunningInHBuilderX(options = {}) {
62
62
  return matchesHBuilderXPluginCwd(options.cwd ?? node_process.default.cwd());
63
63
  }
64
64
  //#endregion
65
+ //#region src/matching.ts
66
+ function evidence(source, key, value, file) {
67
+ return {
68
+ source,
69
+ key,
70
+ ...value !== void 0 ? { value } : {},
71
+ ...file !== void 0 ? { path: file } : {}
72
+ };
73
+ }
74
+ function collectMatches(matches, all) {
75
+ const result = [];
76
+ for (const candidate of matches) {
77
+ result.push(candidate);
78
+ if (!all) break;
79
+ }
80
+ return result;
81
+ }
82
+ function detectionReport(candidates) {
83
+ const grouped = /* @__PURE__ */ new Map();
84
+ for (const candidate of candidates) {
85
+ const existing = grouped.get(candidate.appType);
86
+ if (existing) existing.evidence.push(...candidate.evidence);
87
+ else grouped.set(candidate.appType, {
88
+ ...candidate,
89
+ evidence: [...candidate.evidence]
90
+ });
91
+ }
92
+ const unique = [...grouped.values()];
93
+ const appType = unique[0]?.appType;
94
+ const family = (value) => value.startsWith("uni-app") ? "uni-app" : value;
95
+ return {
96
+ appType,
97
+ candidates: unique,
98
+ conflicts: appType === void 0 ? [] : unique.filter((candidate) => family(candidate.appType) !== family(appType)).map((candidate) => candidate.appType)
99
+ };
100
+ }
101
+ //#endregion
65
102
  //#region src/platform.ts
66
- const MINI_PROGRAM_PLATFORMS = /* @__PURE__ */ new Set([
67
- "alipay",
68
- "baidu",
69
- "jd",
70
- "qq",
71
- "swan",
72
- "tt",
73
- "weapp",
74
- "wechat",
75
- "wx"
103
+ const MPX_TO_UNI_PLATFORM = /* @__PURE__ */ new Map([
104
+ ["ali", "mp-alipay"],
105
+ ["dd", "mp-dingtalk"],
106
+ ["jd", "mp-jd"],
107
+ ["qq", "mp-qq"],
108
+ ["swan", "mp-baidu"],
109
+ ["tt", "mp-toutiao"],
110
+ ["wx", "mp-weixin"]
111
+ ]);
112
+ const MINI_PROGRAM_PLATFORMS = new Map([
113
+ ...[...MPX_TO_UNI_PLATFORM].filter(([alias]) => alias !== "ali" && alias !== "dd"),
114
+ ["alipay", "mp-alipay"],
115
+ ["baidu", "mp-baidu"],
116
+ ["weapp", "mp-weixin"],
117
+ ["wechat", "mp-weixin"]
118
+ ]);
119
+ const CANONICAL_PLATFORMS = new Map([
120
+ ...[...MPX_TO_UNI_PLATFORM.values()].map((value) => [value, value]),
121
+ ["web", "web"],
122
+ ["h5", "web"],
123
+ ["app", "app"],
124
+ ["app-plus", "app-plus"],
125
+ ["app-android", "app-android"],
126
+ ["app-ios", "app-ios"],
127
+ ["app-harmony", "app-harmony"]
76
128
  ]);
77
129
  function normalizePlatform(value) {
78
130
  return value?.trim().toLowerCase() || void 0;
79
131
  }
80
- function resolvePlatformInfo(value) {
132
+ function resolvePlatformInfo(value, appType) {
81
133
  const normalized = normalizePlatform(value);
82
134
  const isAppAndroid = normalized === "app-android";
83
135
  const isAppIos = normalized === "app-ios";
@@ -89,7 +141,7 @@ function resolvePlatformInfo(value) {
89
141
  isAppAndroid,
90
142
  isAppHarmony,
91
143
  isAppIos,
92
- isMp: normalized?.startsWith("mp-") === true || (normalized ? MINI_PROGRAM_PLATFORMS.has(normalized) : false),
144
+ isMp: normalized?.startsWith("mp-") === true || (normalized ? MINI_PROGRAM_PLATFORMS.has(normalized) || appType === "mpx" && MPX_TO_UNI_PLATFORM.has(normalized) : false),
93
145
  isWeb: normalized?.startsWith("web") === true || normalized === "h5"
94
146
  };
95
147
  }
@@ -99,11 +151,19 @@ function resolvePlatform(value) {
99
151
  function resolveTaroPlatform(value = getProcessEnv().TARO_ENV) {
100
152
  return resolvePlatform(value);
101
153
  }
154
+ /** 读取 Taro 环境快照;省略快照时才使用调用时的进程环境。 */
155
+ function resolveTaroPlatformFromEnv(env = getProcessEnv()) {
156
+ return resolvePlatform(env.TARO_ENV);
157
+ }
102
158
  function resolveMpxPlatformValue(env = getProcessEnv()) {
103
159
  return env.MPX_CURRENT_TARGET_MODE ?? env.MPX_CLI_MODE;
104
160
  }
105
161
  function resolveMpxPlatform(value = resolveMpxPlatformValue()) {
106
- return resolvePlatform(value);
162
+ return resolvePlatformInfo(value, "mpx");
163
+ }
164
+ /** 读取 MPX 环境快照,保留 current mode 的空字符串优先级。 */
165
+ function resolveMpxPlatformFromEnv(env = getProcessEnv()) {
166
+ return resolvePlatformInfo(resolveMpxPlatformValue(env), "mpx");
107
167
  }
108
168
  function resolveUniPlatform(value = getProcessEnv().UNI_PLATFORM) {
109
169
  return resolvePlatform(value);
@@ -121,141 +181,474 @@ function resolveUniPlatformsFromEnv(env = getProcessEnv()) {
121
181
  uniUtsPlatform: resolvePlatform(env.UNI_UTS_PLATFORM)
122
182
  };
123
183
  }
124
- const MPX_TO_UNI_PLATFORM = {
125
- ali: "mp-alipay",
126
- dd: "mp-dingtalk",
127
- jd: "mp-jd",
128
- qq: "mp-qq",
129
- swan: "mp-baidu",
130
- tt: "mp-toutiao",
131
- wx: "mp-weixin"
132
- };
133
184
  function normalizeFrameworkStylePlatform(platform, appType) {
134
185
  const normalized = platform?.trim().toLowerCase();
135
186
  if (!normalized) return;
136
- if (appType === "mpx") return MPX_TO_UNI_PLATFORM[normalized] ?? normalized;
187
+ if (appType === "mpx") return MPX_TO_UNI_PLATFORM.get(normalized) ?? normalized;
137
188
  return normalized;
138
189
  }
190
+ function resolvePlatformIdentity(platform, appType) {
191
+ const normalized = normalizePlatform(platform);
192
+ return {
193
+ raw: platform,
194
+ normalized,
195
+ canonical: normalized ? (appType === "mpx" ? MPX_TO_UNI_PLATFORM.get(normalized) : void 0) ?? MINI_PROGRAM_PLATFORMS.get(normalized) ?? CANONICAL_PLATFORMS.get(normalized) : void 0
196
+ };
197
+ }
139
198
  //#endregion
140
199
  //#region src/detect-env.ts
141
- function detectAppTypeFromEnv(env = getProcessEnv(), options = {}) {
142
- if (env.WEAPP_TW_TARGET === "weapp-vite" || env.WEAPP_TAILWINDCSS_TARGET === "weapp-vite") return "weapp-vite";
143
- if (env.TARO_ENV) return "taro";
144
- if (env.MPX_CLI_MODE || env.MPX_CURRENT_TARGET_MODE) return "mpx";
200
+ function* matchEnvironment(env, options = {}) {
201
+ for (const key of ["WEAPP_TW_TARGET", "WEAPP_TAILWINDCSS_TARGET"]) {
202
+ const value = env[key];
203
+ if (value === "weapp-vite") yield {
204
+ appType: "weapp-vite",
205
+ evidence: [evidence("env", key, value)]
206
+ };
207
+ }
208
+ const taro = env.TARO_ENV;
209
+ if (taro) yield {
210
+ appType: "taro",
211
+ evidence: [evidence("env", "TARO_ENV", taro)]
212
+ };
213
+ for (const key of ["MPX_CLI_MODE", "MPX_CURRENT_TARGET_MODE"]) {
214
+ const value = env[key];
215
+ if (value) yield {
216
+ appType: "mpx",
217
+ evidence: [evidence("env", key, value)]
218
+ };
219
+ }
145
220
  const { uniPlatform, uniUtsPlatform } = resolveUniPlatformsFromEnv(env);
146
- if (uniUtsPlatform.normalized) return "uni-app-x";
147
- if (uniPlatform.normalized) return "uni-app-vite";
221
+ if (uniUtsPlatform.normalized) yield {
222
+ appType: "uni-app-x",
223
+ evidence: [evidence("env", "UNI_UTS_PLATFORM", uniUtsPlatform.raw)]
224
+ };
225
+ if (uniPlatform.normalized) yield {
226
+ appType: "uni-app-vite",
227
+ evidence: [evidence("env", "UNI_PLATFORM", uniPlatform.raw)]
228
+ };
148
229
  if (isRunningInHBuilderX({
149
230
  cwd: options.cwd,
150
231
  env
151
- })) return options.hbuilderxVite === false ? "uni-app" : "uni-app-vite";
232
+ })) yield {
233
+ appType: options.hbuilderxVite === false ? "uni-app" : "uni-app-vite",
234
+ evidence: [evidence("hbuilderx", "cwd", options.cwd ?? node_process.default.cwd())]
235
+ };
236
+ }
237
+ function detectAppTypeFromEnv(env = getProcessEnv(), options = {}) {
238
+ return collectMatches(matchEnvironment(env, options), false)[0]?.appType;
239
+ }
240
+ //#endregion
241
+ //#region src/script-command.ts
242
+ const ASSIGNMENT_RE = /^[A-Z_]\w*=/iu;
243
+ const COMMAND_TYPES = /* @__PURE__ */ new Map([
244
+ ["mpx", "mpx"],
245
+ ["mpx-cli-service", "mpx"],
246
+ ["taro", "taro"],
247
+ ["uni", "uni-app-vite"],
248
+ ["weapp-vite", "weapp-vite"]
249
+ ]);
250
+ const PNPM_VALUE_OPTIONS = /* @__PURE__ */ new Set([
251
+ "-C",
252
+ "--dir",
253
+ "-F",
254
+ "--filter",
255
+ "--filter-prod",
256
+ "--reporter"
257
+ ]);
258
+ const PNPM_FLAGS = /* @__PURE__ */ new Set([
259
+ "-w",
260
+ "--workspace-root",
261
+ "-r",
262
+ "--recursive",
263
+ "-s",
264
+ "--silent"
265
+ ]);
266
+ const NPX_VALUE_OPTIONS = /* @__PURE__ */ new Set([
267
+ "-p",
268
+ "--package",
269
+ "--cache",
270
+ "--userconfig"
271
+ ]);
272
+ const NPX_FLAGS = /* @__PURE__ */ new Set([
273
+ "-y",
274
+ "--yes",
275
+ "--no",
276
+ "--no-install",
277
+ "-q",
278
+ "--quiet"
279
+ ]);
280
+ const ENV_VALUE_OPTIONS = /* @__PURE__ */ new Set([
281
+ "-u",
282
+ "--unset",
283
+ "-C",
284
+ "--chdir"
285
+ ]);
286
+ function readExpansionEnd(script, start, opening, closing) {
287
+ let depth = 1;
288
+ let quote;
289
+ for (let index = start + 2; index < script.length; index++) {
290
+ const character = script[index];
291
+ if (character === "\\" && quote !== "'") index++;
292
+ else if (quote) {
293
+ if (character === quote) quote = void 0;
294
+ } else if (character === "\"" || character === "'" || character === "`") quote = character;
295
+ else if (character === opening) depth++;
296
+ else if (character === closing && --depth === 0) return index;
297
+ }
298
+ return -1;
299
+ }
300
+ function tokenizeCommands(script) {
301
+ const commands = [];
302
+ let command = [];
303
+ let value = "";
304
+ let active = false;
305
+ let quoted = false;
306
+ let shellAssignment = false;
307
+ let protectedPrefix = false;
308
+ let quote;
309
+ function flushToken() {
310
+ if (active) {
311
+ command.push({
312
+ value,
313
+ quoted,
314
+ shellAssignment
315
+ });
316
+ value = "";
317
+ active = false;
318
+ quoted = false;
319
+ shellAssignment = false;
320
+ protectedPrefix = false;
321
+ }
322
+ }
323
+ function flushCommand() {
324
+ flushToken();
325
+ if (command.length) {
326
+ commands.push(command);
327
+ command = [];
328
+ }
329
+ }
330
+ for (let index = 0; index < script.length; index++) {
331
+ const character = script[index];
332
+ const next = script[index + 1];
333
+ if (character === "\\" && quote !== "'" && next && /[\s"'\\$`;&|]/u.test(next) || character === "^" && !quote && next && /[\s"^;&|]/u.test(next)) {
334
+ if (next !== "\n" && next !== "\r") {
335
+ protectedPrefix = true;
336
+ value += next;
337
+ active = true;
338
+ } else if (next === "\r" && script[index + 2] === "\n") index++;
339
+ index++;
340
+ } else if (character === "$" && (next === "(" || next === "{") && quote !== "'") {
341
+ const end = readExpansionEnd(script, index, next, next === "(" ? ")" : "}");
342
+ if (end === -1) return [];
343
+ value += script.slice(index, end + 1);
344
+ protectedPrefix = true;
345
+ active = true;
346
+ index = end;
347
+ } else if (quote) {
348
+ if (character === quote) quote = void 0;
349
+ else value += character;
350
+ } else if (character === "\"" || character === "'" || character === "`") {
351
+ quote = character;
352
+ quoted = true;
353
+ protectedPrefix = true;
354
+ active = true;
355
+ if (character === "`") value += character;
356
+ } else if (character === "<" && next === "<") return [];
357
+ else if ((character === ">" || character === "<") && next === "&" || character === "&" && next === ">") {
358
+ value += character + next;
359
+ active = true;
360
+ index++;
361
+ } else if (character === "#" && !active) while (script[index + 1] && script[index + 1] !== "\n") index++;
362
+ else if (character === ";" || character === "&" || character === "|" || character === "\n" || character === "\r") flushCommand();
363
+ else if (/\s/u.test(character)) flushToken();
364
+ else {
365
+ if (character === "=" && !protectedPrefix && ASSIGNMENT_RE.test(`${value}=`)) shellAssignment = true;
366
+ value += character;
367
+ active = true;
368
+ }
369
+ }
370
+ if (quote) return [];
371
+ flushCommand();
372
+ return commands;
373
+ }
374
+ function executableName(value, allowVersion = false) {
375
+ if (allowVersion && value.startsWith("@")) return value;
376
+ const name = node_path.default.win32.basename(node_path.default.posix.basename(value));
377
+ const executable = /\.(?:cmd|bat|exe)$/iu.test(name) ? name.replace(/\.(?:cmd|bat|exe)$/iu, "").toLowerCase() : name;
378
+ return allowVersion ? executable.replace(/@[^@]+$/u, "") : executable;
379
+ }
380
+ function skipAssignments(tokens, index, shellPosition = false) {
381
+ while (tokens[index] && (shellPosition ? tokens[index].shellAssignment : ASSIGNMENT_RE.test(tokens[index].value))) index++;
382
+ return index;
383
+ }
384
+ function skipOptions(tokens, index, flags, valueOptions) {
385
+ while (tokens[index]?.value.startsWith("-")) {
386
+ const value = tokens[index].value;
387
+ if (value === "--") return index + 1;
388
+ if (flags.has(value)) index++;
389
+ else if (valueOptions.has(value)) {
390
+ if (!tokens[index + 1]) return tokens.length;
391
+ index += 2;
392
+ } else if (value.includes("=") && valueOptions.has(value.slice(0, value.indexOf("=")))) index++;
393
+ else return tokens.length;
394
+ }
395
+ return index;
396
+ }
397
+ function collectCommands(script, detected, depth = 0) {
398
+ if (depth > 8) return;
399
+ for (const tokens of tokenizeCommands(script)) {
400
+ let index = skipAssignments(tokens, 0, true);
401
+ let allowVersion = false;
402
+ while (tokens[index]) {
403
+ const name = executableName(tokens[index].value, allowVersion);
404
+ const appType = COMMAND_TYPES.get(name);
405
+ if (appType) {
406
+ detected.add(appType);
407
+ break;
408
+ }
409
+ allowVersion = false;
410
+ index++;
411
+ if (name === "cross-env" || name === "cross-env-shell") {
412
+ index = skipAssignments(tokens, index);
413
+ const payload = tokens[index];
414
+ if (name === "cross-env-shell" && payload?.quoted) {
415
+ collectCommands(payload.value, detected, depth + 1);
416
+ break;
417
+ }
418
+ } else if (name === "env") {
419
+ index = skipOptions(tokens, index, /* @__PURE__ */ new Set(["-i", "--ignore-environment"]), ENV_VALUE_OPTIONS);
420
+ index = skipAssignments(tokens, index);
421
+ } else if (name === "pnpm") {
422
+ index = skipOptions(tokens, index, PNPM_FLAGS, PNPM_VALUE_OPTIONS);
423
+ if (tokens[index]?.value === "exec" || tokens[index]?.value === "dlx") {
424
+ const dlx = tokens[index].value === "dlx";
425
+ index++;
426
+ index = skipOptions(tokens, index, dlx ? NPX_FLAGS : PNPM_FLAGS, dlx ? NPX_VALUE_OPTIONS : PNPM_VALUE_OPTIONS);
427
+ allowVersion = dlx;
428
+ }
429
+ } else if (name === "npx") {
430
+ index = skipOptions(tokens, index, NPX_FLAGS, NPX_VALUE_OPTIONS);
431
+ allowVersion = true;
432
+ } else break;
433
+ }
434
+ }
435
+ }
436
+ /**
437
+ * 仅识别脚本中明确的命令位置,不执行脚本或推断任意参数中的框架名称。
438
+ */
439
+ function detectFrameworkCommands(script) {
440
+ const detected = /* @__PURE__ */ new Set();
441
+ collectCommands(script, detected);
442
+ return [...detected];
152
443
  }
153
444
  //#endregion
154
445
  //#region src/package.ts
155
- const MPX_SCRIPT_RE = /\bmpx(?:-cli-service)?\b/u;
156
- const TARO_SCRIPT_RE = /\btaro\b/u;
157
- const WEAPP_VITE_SCRIPT_RE = /\bweapp-vite\b/u;
158
- const UNI_APP_SCRIPT_RE = /\buni(?:\s|$)/u;
159
- const UNI_APP_VITE_SCRIPT_RE = /\buni(?:\s|$)/u;
160
- function resolveDependencyNames(pkg) {
161
- return /* @__PURE__ */ new Set([
162
- ...Object.keys(pkg.dependencies ?? {}),
163
- ...Object.keys(pkg.devDependencies ?? {}),
164
- ...Object.keys(pkg.peerDependencies ?? {}),
165
- ...Object.keys(pkg.optionalDependencies ?? {})
166
- ]);
167
- }
168
- function hasDependencyPrefix(dependencyNames, prefix) {
169
- return [...dependencyNames].some((name) => name.startsWith(prefix));
170
- }
171
- function hasScriptMatch(pkg, pattern) {
172
- return Object.values(pkg.scripts ?? {}).some((script) => pattern.test(script));
446
+ const FRAMEWORK_PRIORITY = [
447
+ "weapp-vite",
448
+ "mpx",
449
+ "taro",
450
+ "uni-app-x",
451
+ "uni-app-vite",
452
+ "uni-app"
453
+ ];
454
+ const DEPENDENCY_FIELDS = [
455
+ "dependencies",
456
+ "devDependencies",
457
+ "peerDependencies",
458
+ "optionalDependencies"
459
+ ];
460
+ function matchesDependency(appType, name) {
461
+ switch (appType) {
462
+ case "weapp-vite": return name === "weapp-vite" || name.startsWith("@weapp-vite/");
463
+ case "mpx": return name.startsWith("@mpxjs/");
464
+ case "taro": return name.startsWith("@tarojs/");
465
+ case "uni-app-x": return name === "@dcloudio/uni-uts-v1" || name === "@weapp-tailwindcss/debug-uni-app-x";
466
+ case "uni-app-vite": return name === "@dcloudio/vite-plugin-uni";
467
+ case "uni-app": return name === "@dcloudio/vue-cli-plugin-uni" || name === "@dcloudio/uni-app";
468
+ }
469
+ }
470
+ function packageMatcher(pkg, file) {
471
+ let scripts;
472
+ return (appType, all) => {
473
+ const result = [];
474
+ for (const field of DEPENDENCY_FIELDS) for (const [name, version] of Object.entries(pkg[field] ?? {})) if (matchesDependency(appType, name)) {
475
+ result.push(evidence("dependency", `${field}.${name}`, version, file));
476
+ if (!all) return result;
477
+ }
478
+ if (appType === "uni-app" && Object.hasOwn(pkg, "uni-app")) {
479
+ result.push(evidence("package-field", "uni-app", void 0, file));
480
+ if (!all) return result;
481
+ }
482
+ if (appType === "uni-app-x") return result;
483
+ scripts ??= Object.entries(pkg.scripts ?? {}).map(([name, value]) => ({
484
+ name,
485
+ value,
486
+ commands: detectFrameworkCommands(value)
487
+ }));
488
+ for (const script of scripts) {
489
+ const commandType = appType === "uni-app" ? "uni-app-vite" : appType;
490
+ if (script.commands.includes(commandType)) {
491
+ result.push(evidence("script", `scripts.${script.name}`, script.value, file));
492
+ if (!all) return result;
493
+ }
494
+ }
495
+ return result;
496
+ };
497
+ }
498
+ function* matchPackageJson(pkg, all, file) {
499
+ const match = packageMatcher(pkg, file);
500
+ for (const appType of FRAMEWORK_PRIORITY) {
501
+ const evidence = match(appType, all);
502
+ if (evidence.length) yield {
503
+ appType,
504
+ evidence
505
+ };
506
+ }
173
507
  }
174
508
  function isWeappVitePackage(pkg) {
175
- const dependencyNames = resolveDependencyNames(pkg);
176
- return dependencyNames.has("weapp-vite") || hasDependencyPrefix(dependencyNames, "@weapp-vite/") || hasScriptMatch(pkg, WEAPP_VITE_SCRIPT_RE);
509
+ return packageMatcher(pkg)("weapp-vite", false).length > 0;
177
510
  }
178
511
  function isMpxPackage(pkg) {
179
- return hasDependencyPrefix(resolveDependencyNames(pkg), "@mpxjs/") || hasScriptMatch(pkg, MPX_SCRIPT_RE);
512
+ return packageMatcher(pkg)("mpx", false).length > 0;
180
513
  }
181
514
  function isTaroPackage(pkg) {
182
- return hasDependencyPrefix(resolveDependencyNames(pkg), "@tarojs/") || hasScriptMatch(pkg, TARO_SCRIPT_RE);
515
+ return packageMatcher(pkg)("taro", false).length > 0;
183
516
  }
184
517
  function isUniAppVitePackage(pkg) {
185
- return resolveDependencyNames(pkg).has("@dcloudio/vite-plugin-uni") || hasScriptMatch(pkg, UNI_APP_VITE_SCRIPT_RE);
518
+ return packageMatcher(pkg)("uni-app-vite", false).length > 0;
186
519
  }
187
520
  function isUniAppXPackage(pkg) {
188
- const dependencyNames = resolveDependencyNames(pkg);
189
- return dependencyNames.has("@dcloudio/uni-uts-v1") || dependencyNames.has("@weapp-tailwindcss/debug-uni-app-x");
521
+ return packageMatcher(pkg)("uni-app-x", false).length > 0;
190
522
  }
191
523
  function isUniAppPackage(pkg) {
192
- const dependencyNames = resolveDependencyNames(pkg);
193
- return dependencyNames.has("@dcloudio/vue-cli-plugin-uni") || dependencyNames.has("@dcloudio/uni-app") || Object.hasOwn(pkg, "uni-app") || hasScriptMatch(pkg, UNI_APP_SCRIPT_RE);
524
+ return packageMatcher(pkg)("uni-app", false).length > 0;
194
525
  }
195
526
  function isUniAppXManifest(manifest) {
196
527
  return Boolean(manifest && Object.hasOwn(manifest, "uni-app-x"));
197
528
  }
198
529
  function detectAppTypeFromPackageJson(pkg) {
199
- if (isWeappVitePackage(pkg)) return "weapp-vite";
200
- if (isMpxPackage(pkg)) return "mpx";
201
- if (isTaroPackage(pkg)) return "taro";
202
- if (isUniAppXPackage(pkg)) return "uni-app-x";
203
- if (isUniAppVitePackage(pkg)) return "uni-app-vite";
204
- if (isUniAppPackage(pkg)) return "uni-app";
530
+ return collectMatches(matchPackageJson(pkg, false), false)[0]?.appType;
531
+ }
532
+ //#endregion
533
+ //#region src/manifest.ts
534
+ /** 仅移除字符串外的注释,其余语法仍交给严格 JSON 解析。 */
535
+ function parseManifestJson(source) {
536
+ const characters = source.split("");
537
+ let inString = false;
538
+ for (let index = 0; index < source.length; index++) {
539
+ const character = source[index];
540
+ if (inString) {
541
+ if (character === "\\") index++;
542
+ else if (character === "\"") inString = false;
543
+ continue;
544
+ }
545
+ if (character === "\"") {
546
+ inString = true;
547
+ continue;
548
+ }
549
+ if (character !== "/") continue;
550
+ if (source[index + 1] === "/") {
551
+ characters[index] = " ";
552
+ characters[++index] = " ";
553
+ while (index + 1 < source.length && source[index + 1] !== "\n" && source[index + 1] !== "\r") characters[++index] = " ";
554
+ } else if (source[index + 1] === "*") {
555
+ characters[index] = " ";
556
+ characters[++index] = " ";
557
+ let closed = false;
558
+ while (++index < source.length) {
559
+ if (source[index] === "*" && source[index + 1] === "/") {
560
+ characters[index] = " ";
561
+ characters[++index] = " ";
562
+ closed = true;
563
+ break;
564
+ }
565
+ if (source[index] !== "\n" && source[index] !== "\r") characters[index] = " ";
566
+ }
567
+ if (!closed) throw new SyntaxError("manifest.json 中存在未闭合的块注释");
568
+ }
569
+ }
570
+ return JSON.parse(characters.join(""));
205
571
  }
206
572
  //#endregion
207
573
  //#region src/project.ts
208
574
  const PACKAGE_JSON_FILE = "package.json";
209
575
  const MANIFEST_JSON_FILE = "manifest.json";
210
576
  const MPX_MARKERS = [["src", "app.mpx"], ["app.mpx"]];
211
- function tryReadJson(file) {
577
+ function tryReadJson(file, parse = JSON.parse) {
212
578
  if (!(0, node_fs.existsSync)(file)) return;
213
579
  try {
214
- return JSON.parse((0, node_fs.readFileSync)(file, "utf8"));
580
+ return parse((0, node_fs.readFileSync)(file, "utf8"));
215
581
  } catch {}
216
582
  }
217
- function detectAppTypeFromMarkers(root) {
218
- for (const segments of MPX_MARKERS) if ((0, node_fs.existsSync)(node_path.default.join(root, ...segments))) return "mpx";
583
+ function manifestMatches(manifest, file) {
584
+ return isUniAppXManifest(manifest) ? [{
585
+ appType: "uni-app-x",
586
+ evidence: [evidence("manifest", "uni-app-x", void 0, file)]
587
+ }] : [];
219
588
  }
220
- function detectAppTypeFromRoot(root, searchUp = true) {
589
+ function matchRoot(root, searchUp, all) {
221
590
  const resolvedRoot = node_path.default.resolve(root);
222
- if (!(0, node_fs.existsSync)(resolvedRoot)) return;
223
- const markerDetected = detectAppTypeFromMarkers(resolvedRoot);
224
- if (markerDetected) return markerDetected;
591
+ if (!(0, node_fs.existsSync)(resolvedRoot)) return [];
592
+ const markers = [];
593
+ for (const segments of MPX_MARKERS) {
594
+ const file = node_path.default.join(resolvedRoot, ...segments);
595
+ if ((0, node_fs.existsSync)(file)) {
596
+ markers.push(evidence("marker", "app.mpx", void 0, file));
597
+ if (!all) break;
598
+ }
599
+ }
600
+ if (markers.length) return [{
601
+ appType: "mpx",
602
+ evidence: markers
603
+ }];
225
604
  let current = resolvedRoot;
226
605
  while (true) {
227
- if (isUniAppXManifest(tryReadJson(node_path.default.join(current, MANIFEST_JSON_FILE)))) return "uni-app-x";
228
- const pkg = tryReadJson(node_path.default.join(current, PACKAGE_JSON_FILE));
606
+ const manifestFile = node_path.default.join(current, MANIFEST_JSON_FILE);
607
+ const manifest = manifestMatches(tryReadJson(manifestFile, parseManifestJson), manifestFile);
608
+ if (manifest.length) return manifest;
609
+ const packageFile = node_path.default.join(current, PACKAGE_JSON_FILE);
610
+ const pkg = tryReadJson(packageFile);
229
611
  if (pkg) {
230
- const detected = detectAppTypeFromPackageJson(pkg);
231
- if (detected) return detected;
612
+ const candidates = collectMatches(matchPackageJson(pkg, all, packageFile), all);
613
+ if (candidates.length) return candidates;
232
614
  }
233
615
  if (!searchUp) break;
234
616
  const parent = node_path.default.dirname(current);
235
617
  if (parent === current) break;
236
618
  current = parent;
237
619
  }
620
+ return [];
238
621
  }
239
- function detectAppType(options = {}) {
240
- if (isUniAppXManifest(options.manifest)) return "uni-app-x";
622
+ function matchAppType(options, all) {
623
+ const manifest = manifestMatches(options.manifest);
624
+ if (manifest.length) return manifest;
241
625
  if (options.packageJson) {
242
- const detected = detectAppTypeFromPackageJson(options.packageJson);
243
- if (detected) return detected;
626
+ const candidates = collectMatches(matchPackageJson(options.packageJson, all), all);
627
+ if (candidates.length) return candidates;
244
628
  }
245
629
  if (options.root) {
246
- const detected = detectAppTypeFromRoot(options.root, options.searchUp ?? true);
247
- if (detected) return detected;
630
+ const candidates = matchRoot(options.root, options.searchUp ?? true, all);
631
+ if (candidates.length) return candidates;
248
632
  }
249
633
  const env = options.env ?? (options.detectEnv ? getProcessEnv() : void 0);
250
- if (env) return detectAppTypeFromEnv(env, {
634
+ if (!env) return [];
635
+ return collectMatches(matchEnvironment(env, {
251
636
  cwd: options.cwd ?? options.root ?? (options.detectEnv ? node_process.default.cwd() : void 0),
252
637
  hbuilderxVite: options.hbuilderxVite
253
- });
638
+ }), all);
639
+ }
640
+ function detectAppType(options = {}) {
641
+ return matchAppType(options, false)[0]?.appType;
642
+ }
643
+ /** 解释首次命中阶段的框架候选与冲突,不扩大项目搜索范围。 */
644
+ function detectAppTypeReport(options = {}) {
645
+ return detectionReport(matchAppType(options, true));
254
646
  }
255
647
  //#endregion
256
648
  exports.detectAppType = detectAppType;
257
649
  exports.detectAppTypeFromEnv = detectAppTypeFromEnv;
258
650
  exports.detectAppTypeFromPackageJson = detectAppTypeFromPackageJson;
651
+ exports.detectAppTypeReport = detectAppTypeReport;
259
652
  exports.isMpxPackage = isMpxPackage;
260
653
  exports.isRunningInHBuilderX = isRunningInHBuilderX;
261
654
  exports.isTaroPackage = isTaroPackage;
@@ -266,8 +659,11 @@ exports.isUniAppXPackage = isUniAppXPackage;
266
659
  exports.isWeappVitePackage = isWeappVitePackage;
267
660
  exports.normalizeFrameworkStylePlatform = normalizeFrameworkStylePlatform;
268
661
  exports.resolveMpxPlatform = resolveMpxPlatform;
662
+ exports.resolveMpxPlatformFromEnv = resolveMpxPlatformFromEnv;
269
663
  exports.resolvePlatform = resolvePlatform;
664
+ exports.resolvePlatformIdentity = resolvePlatformIdentity;
270
665
  exports.resolveTaroPlatform = resolveTaroPlatform;
666
+ exports.resolveTaroPlatformFromEnv = resolveTaroPlatformFromEnv;
271
667
  exports.resolveUniAppXPlatform = resolveUniAppXPlatform;
272
668
  exports.resolveUniPlatform = resolveUniPlatform;
273
669
  exports.resolveUniPlatformsFromEnv = resolveUniPlatformsFromEnv;