teamix-evo 0.20.1 → 0.20.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -170,7 +170,7 @@ TEAMIX_DEBUG=1 teamix-evo tokens init opentrek
170
170
 
171
171
  | 命令 | 说明 |
172
172
  | --------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
173
- | `teamix-evo init [-y] [--dry-run] [--variant <n>]` | 普通版接入:检测冲突 → wizard → 静默落地(已有 npm 工程入口) |
173
+ | `teamix-evo init [-y] [--dry-run] [--variant <n>]` | 普通版接入:检测冲突 → wizard → 静默落地 自动提交,并校验默认 Git 工作区干净 |
174
174
  | `teamix-evo update [--dry-run] [--cwd <dir>]` | 一键升级已装资源(tokens + skills,ADR 0003 三态 + ADR 0035 短路) |
175
175
  | `teamix-evo migrate [--cwd <dir>] [--json]` | shadcn 项目迁移前置检查(AI skill 引导实际迁移) |
176
176
  | `teamix-evo graft [--variant <v>] [--json] [--cwd <dir>]` | 叠加 Teamix Evo 到传统组件库项目(双栈共存,ADR 0047) |
@@ -2027,6 +2027,7 @@ async function loadUiData(packageName) {
2027
2027
  // src/core/ui-installer.ts
2028
2028
  import * as path12 from "path";
2029
2029
  import * as fs10 from "fs/promises";
2030
+ import { isBuiltin } from "module";
2030
2031
  import { resolveUiEntryOrder } from "@teamix-evo/registry";
2031
2032
 
2032
2033
  // src/utils/transform-imports.ts
@@ -2080,6 +2081,12 @@ async function installUiEntries(options) {
2080
2081
  } = options;
2081
2082
  const orderedIds = resolveUiEntryOrder(manifest.entries, requested);
2082
2083
  const idToEntry = new Map(manifest.entries.map((e) => [e.id, e]));
2084
+ const sourceContents = await preflightUiEntrySources({
2085
+ manifest,
2086
+ orderedIds,
2087
+ packageRoot,
2088
+ entryPackageRoot
2089
+ });
2083
2090
  const resources = [];
2084
2091
  const npmDeps = {};
2085
2092
  let created = 0;
@@ -2108,7 +2115,12 @@ async function installUiEntries(options) {
2108
2115
  }
2109
2116
  const rootForEntry = entryPackageRoot?.get(entry.id) ?? packageRoot;
2110
2117
  const sourceAbs = path12.resolve(rootForEntry, file.source);
2111
- const raw = await fs10.readFile(sourceAbs, "utf-8");
2118
+ const raw = sourceContents.get(sourceAbs);
2119
+ if (raw === void 0) {
2120
+ throw new Error(
2121
+ `UI install preflight did not load source file "${file.source}" for entry "${entry.id}". No files were written.`
2122
+ );
2123
+ }
2112
2124
  const transformed = rewriteImports(raw, aliases, { flatten });
2113
2125
  if (exists) {
2114
2126
  const current = await fs10.readFile(targetAbs, "utf-8");
@@ -2161,6 +2173,189 @@ async function installUiEntries(options) {
2161
2173
  skipped
2162
2174
  };
2163
2175
  }
2176
+ var PROJECT_RUNTIME_PACKAGES = /* @__PURE__ */ new Set(["react", "react-dom"]);
2177
+ async function preflightUiEntrySources(options) {
2178
+ const { manifest, orderedIds, packageRoot, entryPackageRoot } = options;
2179
+ const sourceContents = /* @__PURE__ */ new Map();
2180
+ const aliasProviders = buildSourceAliasProviders(manifest.entries);
2181
+ const sourceProviders = buildRelativeSourceProviders(
2182
+ manifest.entries,
2183
+ packageRoot,
2184
+ entryPackageRoot
2185
+ );
2186
+ const issues = [];
2187
+ for (const id of orderedIds) {
2188
+ const entry = manifest.entries.find((candidate) => candidate.id === id);
2189
+ if (!entry) continue;
2190
+ const declaredRegistry = new Set(entry.registryDependencies ?? []);
2191
+ const declaredPackages = new Set(Object.keys(entry.dependencies ?? {}));
2192
+ const rootForEntry = entryPackageRoot?.get(entry.id) ?? packageRoot;
2193
+ for (const file of entry.files) {
2194
+ const sourceAbs = path12.resolve(rootForEntry, file.source);
2195
+ const source = await fs10.readFile(sourceAbs, "utf-8");
2196
+ sourceContents.set(sourceAbs, source);
2197
+ if (manifest.package !== "ui") continue;
2198
+ for (const specifier of extractModuleSpecifiers(source)) {
2199
+ if (specifier.startsWith("@/")) {
2200
+ const provider = aliasProviders.get(specifier);
2201
+ if (!provider) {
2202
+ issues.push({
2203
+ entryId: entry.id,
2204
+ source: file.source,
2205
+ message: `unresolved registry import "${specifier}"`
2206
+ });
2207
+ } else if (provider !== entry.id && !declaredRegistry.has(provider)) {
2208
+ issues.push({
2209
+ entryId: entry.id,
2210
+ source: file.source,
2211
+ message: `undeclared registry dependency "${provider}" (import "${specifier}")`
2212
+ });
2213
+ }
2214
+ continue;
2215
+ }
2216
+ if (specifier.startsWith(".")) {
2217
+ const provider = resolveRelativeProvider(
2218
+ path12.resolve(path12.dirname(sourceAbs), specifier),
2219
+ sourceProviders
2220
+ );
2221
+ if (!provider) {
2222
+ issues.push({
2223
+ entryId: entry.id,
2224
+ source: file.source,
2225
+ message: `unshipped or unresolved relative import "${specifier}"`
2226
+ });
2227
+ } else if (provider !== entry.id && !declaredRegistry.has(provider)) {
2228
+ issues.push({
2229
+ entryId: entry.id,
2230
+ source: file.source,
2231
+ message: `undeclared registry dependency "${provider}" (relative import "${specifier}")`
2232
+ });
2233
+ }
2234
+ continue;
2235
+ }
2236
+ const packageName = externalPackageName(specifier);
2237
+ if (!isBuiltin(specifier) && !PROJECT_RUNTIME_PACKAGES.has(packageName) && !declaredPackages.has(packageName)) {
2238
+ issues.push({
2239
+ entryId: entry.id,
2240
+ source: file.source,
2241
+ message: `undeclared npm dependency "${packageName}" (import "${specifier}")`
2242
+ });
2243
+ }
2244
+ }
2245
+ }
2246
+ }
2247
+ if (issues.length > 0) {
2248
+ const details = issues.sort(
2249
+ (a, b) => `${a.entryId}:${a.source}:${a.message}`.localeCompare(
2250
+ `${b.entryId}:${b.source}:${b.message}`
2251
+ )
2252
+ ).map(
2253
+ (issue) => `- Entry "${issue.entryId}" (${issue.source}): ${issue.message}`
2254
+ ).join("\n");
2255
+ throw new Error(
2256
+ [
2257
+ "UI install preflight failed. No files were written.",
2258
+ details,
2259
+ "Fix registryDependencies/dependencies in the package manifest, publish the corrected package, and retry."
2260
+ ].join("\n")
2261
+ );
2262
+ }
2263
+ return sourceContents;
2264
+ }
2265
+ function extractModuleSpecifiers(source) {
2266
+ const specifiers = /* @__PURE__ */ new Set();
2267
+ const staticPattern = /\b(?:import|export)\s+(?:type\s+)?(?:[^'";]*?\s+from\s+)?(['"])([^'"]+)\1/g;
2268
+ const dynamicPattern = /\bimport\s*\(\s*(['"])([^'"]+)\1\s*\)/g;
2269
+ for (const pattern of [staticPattern, dynamicPattern]) {
2270
+ let match;
2271
+ while ((match = pattern.exec(source)) !== null) {
2272
+ if (match[2]) specifiers.add(match[2]);
2273
+ }
2274
+ }
2275
+ return specifiers;
2276
+ }
2277
+ function buildSourceAliasProviders(entries) {
2278
+ const providers = /* @__PURE__ */ new Map();
2279
+ for (const entry of entries) {
2280
+ for (const file of entry.files) {
2281
+ const target = stripModuleExtension(file.targetName);
2282
+ const aliases = sourceAliases(file.targetAlias, target);
2283
+ for (const alias of aliases) {
2284
+ const current = providers.get(alias);
2285
+ if (current && current !== entry.id) {
2286
+ throw new Error(
2287
+ `UI install preflight found ambiguous source alias "${alias}" for entries "${current}" and "${entry.id}". No files were written.`
2288
+ );
2289
+ }
2290
+ providers.set(alias, entry.id);
2291
+ }
2292
+ }
2293
+ }
2294
+ return providers;
2295
+ }
2296
+ function sourceAliases(targetAlias, target) {
2297
+ switch (targetAlias) {
2298
+ case "components":
2299
+ return [`@/components/${target}`, `@/components/ui/${target}`];
2300
+ case "business":
2301
+ return [
2302
+ `@/components/business/${target}`,
2303
+ `@/business/${target}`
2304
+ ];
2305
+ case "hooks":
2306
+ return [`@/hooks/${target}`];
2307
+ case "utils":
2308
+ return [`@/utils/${target}`, `@/lib/utils/${target}`];
2309
+ case "lib":
2310
+ return [`@/lib/${target}`];
2311
+ case "blocks":
2312
+ return [`@/blocks/${target}`];
2313
+ default:
2314
+ return [];
2315
+ }
2316
+ }
2317
+ function buildRelativeSourceProviders(entries, packageRoot, entryPackageRoot) {
2318
+ const providers = /* @__PURE__ */ new Map();
2319
+ for (const entry of entries) {
2320
+ const rootForEntry = entryPackageRoot?.get(entry.id) ?? packageRoot;
2321
+ for (const file of entry.files) {
2322
+ const absolute = path12.resolve(rootForEntry, file.source);
2323
+ for (const candidate of relativeSourceCandidates(absolute)) {
2324
+ providers.set(candidate, entry.id);
2325
+ }
2326
+ }
2327
+ }
2328
+ return providers;
2329
+ }
2330
+ function resolveRelativeProvider(absolute, providers) {
2331
+ for (const candidate of relativeSourceCandidates(absolute)) {
2332
+ const provider = providers.get(candidate);
2333
+ if (provider) return provider;
2334
+ }
2335
+ return void 0;
2336
+ }
2337
+ function relativeSourceCandidates(absolute) {
2338
+ const extension = path12.extname(absolute);
2339
+ if (extension) return [absolute];
2340
+ return [
2341
+ absolute,
2342
+ `${absolute}.ts`,
2343
+ `${absolute}.tsx`,
2344
+ `${absolute}.js`,
2345
+ `${absolute}.jsx`,
2346
+ path12.join(absolute, "index.ts"),
2347
+ path12.join(absolute, "index.tsx"),
2348
+ path12.join(absolute, "index.js"),
2349
+ path12.join(absolute, "index.jsx")
2350
+ ];
2351
+ }
2352
+ function stripModuleExtension(value) {
2353
+ return value.replace(/\.(?:[cm]?[jt]sx?)$/, "");
2354
+ }
2355
+ function externalPackageName(specifier) {
2356
+ const parts = specifier.split("/");
2357
+ return specifier.startsWith("@") ? parts.slice(0, 2).join("/") : parts[0] ?? specifier;
2358
+ }
2164
2359
  function resolveTargetPath(projectRoot, aliases, entry, file) {
2165
2360
  const aliasDir = aliases[file.targetAlias];
2166
2361
  if (!aliasDir) {
@@ -2502,7 +2697,16 @@ async function listBizUiEntries(variant, packageRoot) {
2502
2697
  import * as path14 from "path";
2503
2698
  import * as fs12 from "fs";
2504
2699
  import { execa } from "execa";
2505
- var ESLINT_CONFIG_CONTENT = `/**
2700
+ function renderEslintConfig(tailwindCssConfigPath) {
2701
+ const tailwindSettings = tailwindCssConfigPath ? `
2702
+ {
2703
+ settings: {
2704
+ tailwindcss: {
2705
+ cssConfigPath: '${tailwindCssConfigPath}',
2706
+ },
2707
+ },
2708
+ },` : "";
2709
+ return `/**
2506
2710
  * teamix-evo consumer ESLint preset \u2014 9 token-discipline rules.
2507
2711
  * - Repo-wide: no-color-literal / no-arbitrary-tw-value / no-raw-color-scale /
2508
2712
  * no-large-radius / prefer-gap-over-space / no-manual-dark-classnames /
@@ -2513,8 +2717,30 @@ var ESLINT_CONFIG_CONTENT = `/**
2513
2717
  */
2514
2718
  import consumerPreset from '@teamix-evo/eslint-config/presets/consumer';
2515
2719
 
2516
- export default [...consumerPreset];
2720
+ export default [
2721
+ ...consumerPreset,${tailwindSettings}
2722
+ ];
2517
2723
  `;
2724
+ }
2725
+ var TAILWIND_CSS_CANDIDATES = [
2726
+ "src/index.css",
2727
+ "src/globals.css",
2728
+ "src/app/globals.css",
2729
+ "app/globals.css",
2730
+ "styles/globals.css",
2731
+ "src/styles/tailwind.css"
2732
+ ];
2733
+ async function detectTailwindCssConfigPath(projectRoot) {
2734
+ for (const candidate of TAILWIND_CSS_CANDIDATES) {
2735
+ const content = await readFileOrNull(path14.join(projectRoot, candidate));
2736
+ if (content && /@(?:import\s+['"]tailwindcss(?:\/[^'"]*)?['"]|theme\b|tailwind\b)/.test(
2737
+ content
2738
+ )) {
2739
+ return `./${candidate}`;
2740
+ }
2741
+ }
2742
+ return null;
2743
+ }
2518
2744
  var STYLELINT_CONFIG_CONTENT = `/** @type {import('stylelint').Config} */
2519
2745
  module.exports = {
2520
2746
  extends: ['@teamix-evo/stylelint-config/presets/consumer'],
@@ -2579,7 +2805,13 @@ async function runLintInit(options) {
2579
2805
  let wroteEslint = false;
2580
2806
  let wroteStylelint = false;
2581
2807
  if (eslintNeedsWrite) {
2582
- await writeFileSafe(eslintConfigPath, ESLINT_CONFIG_CONTENT);
2808
+ const tailwindCssConfigPath = await detectTailwindCssConfigPath(
2809
+ projectRoot
2810
+ );
2811
+ await writeFileSafe(
2812
+ eslintConfigPath,
2813
+ renderEslintConfig(tailwindCssConfigPath)
2814
+ );
2583
2815
  logger.debug(`Wrote eslint.config.js \u2192 ${eslintConfigPath}`);
2584
2816
  wroteEslint = true;
2585
2817
  }