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/dist/index.js CHANGED
@@ -5411,6 +5411,7 @@ async function loadUiData(packageName) {
5411
5411
  init_fs();
5412
5412
  import * as path25 from "path";
5413
5413
  import * as fs20 from "fs/promises";
5414
+ import { isBuiltin } from "module";
5414
5415
  import { resolveUiEntryOrder } from "@teamix-evo/registry";
5415
5416
 
5416
5417
  // src/utils/transform-imports.ts
@@ -5466,6 +5467,12 @@ async function installUiEntries(options) {
5466
5467
  } = options;
5467
5468
  const orderedIds = resolveUiEntryOrder(manifest.entries, requested);
5468
5469
  const idToEntry = new Map(manifest.entries.map((e) => [e.id, e]));
5470
+ const sourceContents = await preflightUiEntrySources({
5471
+ manifest,
5472
+ orderedIds,
5473
+ packageRoot,
5474
+ entryPackageRoot
5475
+ });
5469
5476
  const resources = [];
5470
5477
  const npmDeps = {};
5471
5478
  let created = 0;
@@ -5494,7 +5501,12 @@ async function installUiEntries(options) {
5494
5501
  }
5495
5502
  const rootForEntry = entryPackageRoot?.get(entry.id) ?? packageRoot;
5496
5503
  const sourceAbs = path25.resolve(rootForEntry, file.source);
5497
- const raw = await fs20.readFile(sourceAbs, "utf-8");
5504
+ const raw = sourceContents.get(sourceAbs);
5505
+ if (raw === void 0) {
5506
+ throw new Error(
5507
+ `UI install preflight did not load source file "${file.source}" for entry "${entry.id}". No files were written.`
5508
+ );
5509
+ }
5498
5510
  const transformed = rewriteImports(raw, aliases, { flatten });
5499
5511
  if (exists) {
5500
5512
  const current = await fs20.readFile(targetAbs, "utf-8");
@@ -5547,6 +5559,189 @@ async function installUiEntries(options) {
5547
5559
  skipped
5548
5560
  };
5549
5561
  }
5562
+ var PROJECT_RUNTIME_PACKAGES = /* @__PURE__ */ new Set(["react", "react-dom"]);
5563
+ async function preflightUiEntrySources(options) {
5564
+ const { manifest, orderedIds, packageRoot, entryPackageRoot } = options;
5565
+ const sourceContents = /* @__PURE__ */ new Map();
5566
+ const aliasProviders = buildSourceAliasProviders(manifest.entries);
5567
+ const sourceProviders = buildRelativeSourceProviders(
5568
+ manifest.entries,
5569
+ packageRoot,
5570
+ entryPackageRoot
5571
+ );
5572
+ const issues = [];
5573
+ for (const id of orderedIds) {
5574
+ const entry = manifest.entries.find((candidate) => candidate.id === id);
5575
+ if (!entry) continue;
5576
+ const declaredRegistry = new Set(entry.registryDependencies ?? []);
5577
+ const declaredPackages = new Set(Object.keys(entry.dependencies ?? {}));
5578
+ const rootForEntry = entryPackageRoot?.get(entry.id) ?? packageRoot;
5579
+ for (const file of entry.files) {
5580
+ const sourceAbs = path25.resolve(rootForEntry, file.source);
5581
+ const source = await fs20.readFile(sourceAbs, "utf-8");
5582
+ sourceContents.set(sourceAbs, source);
5583
+ if (manifest.package !== "ui") continue;
5584
+ for (const specifier of extractModuleSpecifiers(source)) {
5585
+ if (specifier.startsWith("@/")) {
5586
+ const provider = aliasProviders.get(specifier);
5587
+ if (!provider) {
5588
+ issues.push({
5589
+ entryId: entry.id,
5590
+ source: file.source,
5591
+ message: `unresolved registry import "${specifier}"`
5592
+ });
5593
+ } else if (provider !== entry.id && !declaredRegistry.has(provider)) {
5594
+ issues.push({
5595
+ entryId: entry.id,
5596
+ source: file.source,
5597
+ message: `undeclared registry dependency "${provider}" (import "${specifier}")`
5598
+ });
5599
+ }
5600
+ continue;
5601
+ }
5602
+ if (specifier.startsWith(".")) {
5603
+ const provider = resolveRelativeProvider(
5604
+ path25.resolve(path25.dirname(sourceAbs), specifier),
5605
+ sourceProviders
5606
+ );
5607
+ if (!provider) {
5608
+ issues.push({
5609
+ entryId: entry.id,
5610
+ source: file.source,
5611
+ message: `unshipped or unresolved relative import "${specifier}"`
5612
+ });
5613
+ } else if (provider !== entry.id && !declaredRegistry.has(provider)) {
5614
+ issues.push({
5615
+ entryId: entry.id,
5616
+ source: file.source,
5617
+ message: `undeclared registry dependency "${provider}" (relative import "${specifier}")`
5618
+ });
5619
+ }
5620
+ continue;
5621
+ }
5622
+ const packageName = externalPackageName(specifier);
5623
+ if (!isBuiltin(specifier) && !PROJECT_RUNTIME_PACKAGES.has(packageName) && !declaredPackages.has(packageName)) {
5624
+ issues.push({
5625
+ entryId: entry.id,
5626
+ source: file.source,
5627
+ message: `undeclared npm dependency "${packageName}" (import "${specifier}")`
5628
+ });
5629
+ }
5630
+ }
5631
+ }
5632
+ }
5633
+ if (issues.length > 0) {
5634
+ const details = issues.sort(
5635
+ (a, b) => `${a.entryId}:${a.source}:${a.message}`.localeCompare(
5636
+ `${b.entryId}:${b.source}:${b.message}`
5637
+ )
5638
+ ).map(
5639
+ (issue) => `- Entry "${issue.entryId}" (${issue.source}): ${issue.message}`
5640
+ ).join("\n");
5641
+ throw new Error(
5642
+ [
5643
+ "UI install preflight failed. No files were written.",
5644
+ details,
5645
+ "Fix registryDependencies/dependencies in the package manifest, publish the corrected package, and retry."
5646
+ ].join("\n")
5647
+ );
5648
+ }
5649
+ return sourceContents;
5650
+ }
5651
+ function extractModuleSpecifiers(source) {
5652
+ const specifiers = /* @__PURE__ */ new Set();
5653
+ const staticPattern = /\b(?:import|export)\s+(?:type\s+)?(?:[^'";]*?\s+from\s+)?(['"])([^'"]+)\1/g;
5654
+ const dynamicPattern = /\bimport\s*\(\s*(['"])([^'"]+)\1\s*\)/g;
5655
+ for (const pattern of [staticPattern, dynamicPattern]) {
5656
+ let match;
5657
+ while ((match = pattern.exec(source)) !== null) {
5658
+ if (match[2]) specifiers.add(match[2]);
5659
+ }
5660
+ }
5661
+ return specifiers;
5662
+ }
5663
+ function buildSourceAliasProviders(entries) {
5664
+ const providers = /* @__PURE__ */ new Map();
5665
+ for (const entry of entries) {
5666
+ for (const file of entry.files) {
5667
+ const target = stripModuleExtension(file.targetName);
5668
+ const aliases = sourceAliases(file.targetAlias, target);
5669
+ for (const alias of aliases) {
5670
+ const current = providers.get(alias);
5671
+ if (current && current !== entry.id) {
5672
+ throw new Error(
5673
+ `UI install preflight found ambiguous source alias "${alias}" for entries "${current}" and "${entry.id}". No files were written.`
5674
+ );
5675
+ }
5676
+ providers.set(alias, entry.id);
5677
+ }
5678
+ }
5679
+ }
5680
+ return providers;
5681
+ }
5682
+ function sourceAliases(targetAlias, target) {
5683
+ switch (targetAlias) {
5684
+ case "components":
5685
+ return [`@/components/${target}`, `@/components/ui/${target}`];
5686
+ case "business":
5687
+ return [
5688
+ `@/components/business/${target}`,
5689
+ `@/business/${target}`
5690
+ ];
5691
+ case "hooks":
5692
+ return [`@/hooks/${target}`];
5693
+ case "utils":
5694
+ return [`@/utils/${target}`, `@/lib/utils/${target}`];
5695
+ case "lib":
5696
+ return [`@/lib/${target}`];
5697
+ case "blocks":
5698
+ return [`@/blocks/${target}`];
5699
+ default:
5700
+ return [];
5701
+ }
5702
+ }
5703
+ function buildRelativeSourceProviders(entries, packageRoot, entryPackageRoot) {
5704
+ const providers = /* @__PURE__ */ new Map();
5705
+ for (const entry of entries) {
5706
+ const rootForEntry = entryPackageRoot?.get(entry.id) ?? packageRoot;
5707
+ for (const file of entry.files) {
5708
+ const absolute = path25.resolve(rootForEntry, file.source);
5709
+ for (const candidate of relativeSourceCandidates(absolute)) {
5710
+ providers.set(candidate, entry.id);
5711
+ }
5712
+ }
5713
+ }
5714
+ return providers;
5715
+ }
5716
+ function resolveRelativeProvider(absolute, providers) {
5717
+ for (const candidate of relativeSourceCandidates(absolute)) {
5718
+ const provider = providers.get(candidate);
5719
+ if (provider) return provider;
5720
+ }
5721
+ return void 0;
5722
+ }
5723
+ function relativeSourceCandidates(absolute) {
5724
+ const extension = path25.extname(absolute);
5725
+ if (extension) return [absolute];
5726
+ return [
5727
+ absolute,
5728
+ `${absolute}.ts`,
5729
+ `${absolute}.tsx`,
5730
+ `${absolute}.js`,
5731
+ `${absolute}.jsx`,
5732
+ path25.join(absolute, "index.ts"),
5733
+ path25.join(absolute, "index.tsx"),
5734
+ path25.join(absolute, "index.js"),
5735
+ path25.join(absolute, "index.jsx")
5736
+ ];
5737
+ }
5738
+ function stripModuleExtension(value) {
5739
+ return value.replace(/\.(?:[cm]?[jt]sx?)$/, "");
5740
+ }
5741
+ function externalPackageName(specifier) {
5742
+ const parts = specifier.split("/");
5743
+ return specifier.startsWith("@") ? parts.slice(0, 2).join("/") : parts[0] ?? specifier;
5744
+ }
5550
5745
  function resolveTargetPath(projectRoot, aliases, entry, file) {
5551
5746
  const aliasDir = aliases[file.targetAlias];
5552
5747
  if (!aliasDir) {
@@ -8356,7 +8551,16 @@ init_logger();
8356
8551
  import * as path35 from "path";
8357
8552
  import * as fs29 from "fs";
8358
8553
  import { execa } from "execa";
8359
- var ESLINT_CONFIG_CONTENT = `/**
8554
+ function renderEslintConfig(tailwindCssConfigPath) {
8555
+ const tailwindSettings = tailwindCssConfigPath ? `
8556
+ {
8557
+ settings: {
8558
+ tailwindcss: {
8559
+ cssConfigPath: '${tailwindCssConfigPath}',
8560
+ },
8561
+ },
8562
+ },` : "";
8563
+ return `/**
8360
8564
  * teamix-evo consumer ESLint preset \u2014 9 token-discipline rules.
8361
8565
  * - Repo-wide: no-color-literal / no-arbitrary-tw-value / no-raw-color-scale /
8362
8566
  * no-large-radius / prefer-gap-over-space / no-manual-dark-classnames /
@@ -8367,8 +8571,30 @@ var ESLINT_CONFIG_CONTENT = `/**
8367
8571
  */
8368
8572
  import consumerPreset from '@teamix-evo/eslint-config/presets/consumer';
8369
8573
 
8370
- export default [...consumerPreset];
8574
+ export default [
8575
+ ...consumerPreset,${tailwindSettings}
8576
+ ];
8371
8577
  `;
8578
+ }
8579
+ var TAILWIND_CSS_CANDIDATES = [
8580
+ "src/index.css",
8581
+ "src/globals.css",
8582
+ "src/app/globals.css",
8583
+ "app/globals.css",
8584
+ "styles/globals.css",
8585
+ "src/styles/tailwind.css"
8586
+ ];
8587
+ async function detectTailwindCssConfigPath(projectRoot) {
8588
+ for (const candidate of TAILWIND_CSS_CANDIDATES) {
8589
+ const content = await readFileOrNull(path35.join(projectRoot, candidate));
8590
+ if (content && /@(?:import\s+['"]tailwindcss(?:\/[^'"]*)?['"]|theme\b|tailwind\b)/.test(
8591
+ content
8592
+ )) {
8593
+ return `./${candidate}`;
8594
+ }
8595
+ }
8596
+ return null;
8597
+ }
8372
8598
  var STYLELINT_CONFIG_CONTENT = `/** @type {import('stylelint').Config} */
8373
8599
  module.exports = {
8374
8600
  extends: ['@teamix-evo/stylelint-config/presets/consumer'],
@@ -8433,7 +8659,13 @@ async function runLintInit(options) {
8433
8659
  let wroteEslint = false;
8434
8660
  let wroteStylelint = false;
8435
8661
  if (eslintNeedsWrite) {
8436
- await writeFileSafe(eslintConfigPath, ESLINT_CONFIG_CONTENT);
8662
+ const tailwindCssConfigPath = await detectTailwindCssConfigPath(
8663
+ projectRoot
8664
+ );
8665
+ await writeFileSafe(
8666
+ eslintConfigPath,
8667
+ renderEslintConfig(tailwindCssConfigPath)
8668
+ );
8437
8669
  logger.debug(`Wrote eslint.config.js \u2192 ${eslintConfigPath}`);
8438
8670
  wroteEslint = true;
8439
8671
  }
@@ -8575,6 +8807,7 @@ lintCommand.addCommand(initCommand4);
8575
8807
  // src/commands/init/index.ts
8576
8808
  import { Command as Command36 } from "commander";
8577
8809
  import * as path39 from "path";
8810
+ import { realpath } from "fs/promises";
8578
8811
  import { execa as execa2 } from "execa";
8579
8812
 
8580
8813
  // src/core/project-state.ts
@@ -9493,7 +9726,7 @@ var initCommand5 = new Command36("init").description("\u521D\u59CB\u5316 Teamix
9493
9726
  variant: answers.variant,
9494
9727
  ides: answers.ides,
9495
9728
  force: opts.force,
9496
- skipInstall: false,
9729
+ skipInstall: shouldSkipInstall(opts.install),
9497
9730
  dryRun,
9498
9731
  onStep: (step) => {
9499
9732
  const icon = STEP_ICON[step.status];
@@ -9533,7 +9766,9 @@ var initCommand5 = new Command36("init").description("\u521D\u59CB\u5316 Teamix
9533
9766
  }
9534
9767
  }
9535
9768
  if (result.status === "installed") {
9536
- await autoCommitAfterInit(cwd);
9769
+ if (shouldAutoCommitAfterInit(state.state, opts.git)) {
9770
+ await autoCommitAfterInit(cwd);
9771
+ }
9537
9772
  logger.info("");
9538
9773
  logger.info("Next steps:");
9539
9774
  logger.info(" \u2022 \u542F\u52A8 dev server \u9A8C\u8BC1\uFF1A`npm run dev`");
@@ -9596,12 +9831,30 @@ async function runScaffoldPhase(cwd, variant, opts) {
9596
9831
  }
9597
9832
  logger.info("");
9598
9833
  }
9834
+ function shouldAutoCommitAfterInit(initialState, gitEnabled) {
9835
+ return initialState === "empty" && gitEnabled !== false;
9836
+ }
9837
+ function shouldSkipInstall(install) {
9838
+ return install === false;
9839
+ }
9599
9840
  async function autoCommitAfterInit(cwd) {
9841
+ let gitTopLevel;
9842
+ let canonicalCwd;
9600
9843
  try {
9601
- await execa2("git", ["rev-parse", "--is-inside-work-tree"], { cwd });
9844
+ const result = await execa2("git", ["rev-parse", "--show-toplevel"], { cwd });
9845
+ [gitTopLevel, canonicalCwd] = await Promise.all([
9846
+ realpath(result.stdout.trim()),
9847
+ realpath(cwd)
9848
+ ]);
9602
9849
  } catch {
9603
9850
  return;
9604
9851
  }
9852
+ if (gitTopLevel !== canonicalCwd) {
9853
+ logger.warn(
9854
+ "\u8DF3\u8FC7\u81EA\u52A8\u63D0\u4EA4\uFF1A\u76EE\u6807\u76EE\u5F55\u4E0D\u662F\u72EC\u7ACB Git \u4ED3\u5E93\uFF0C\u907F\u514D\u8BEF\u63D0\u4EA4\u7236\u4ED3\u5E93\u5185\u5BB9\u3002"
9855
+ );
9856
+ return;
9857
+ }
9605
9858
  try {
9606
9859
  await execa2("git", ["add", "."], { cwd });
9607
9860
  await execa2(
@@ -9609,8 +9862,23 @@ async function autoCommitAfterInit(cwd) {
9609
9862
  ["commit", "-m", "chore: init teamix-evo", "--allow-empty"],
9610
9863
  { cwd }
9611
9864
  );
9865
+ const { stdout: worktreeStatus } = await execa2(
9866
+ "git",
9867
+ ["status", "--porcelain", "--untracked-files=all"],
9868
+ { cwd }
9869
+ );
9612
9870
  logger.info("");
9613
- logger.success("\u5DF2\u81EA\u52A8\u63D0\u4EA4 init \u4EA7\u7269\uFF1Achore: init teamix-evo");
9871
+ if (worktreeStatus.trim()) {
9872
+ logger.warn("init \u5DF2\u81EA\u52A8\u63D0\u4EA4\uFF0C\u4F46\u5DE5\u4F5C\u533A\u4ECD\u6709\u672A\u63D0\u4EA4\u5185\u5BB9\uFF1A");
9873
+ for (const line of worktreeStatus.trim().split("\n")) {
9874
+ logger.warn(` ${line}`);
9875
+ }
9876
+ logger.warn("\u8BF7\u5904\u7406\u4E0A\u8FF0\u6587\u4EF6\u540E\u518D\u6B21\u786E\u8BA4 `git status --short` \u4E3A\u7A7A\u3002");
9877
+ } else {
9878
+ logger.success(
9879
+ "\u5DF2\u81EA\u52A8\u63D0\u4EA4 init \u4EA7\u7269\uFF1Achore: init teamix-evo\uFF08\u5DE5\u4F5C\u533A\u5E72\u51C0\uFF09"
9880
+ );
9881
+ }
9614
9882
  } catch (err) {
9615
9883
  logger.warn(
9616
9884
  `\u81EA\u52A8\u63D0\u4EA4\u5931\u8D25\uFF08\u975E\u5173\u952E\uFF09\uFF1A${err instanceof Error ? err.message : String(err)}`