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