poe-code 3.0.392 → 3.0.393

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "poe-code",
3
- "version": "3.0.392",
3
+ "version": "3.0.393",
4
4
  "description": "CLI tool to configure Poe API for developer workflows.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -34,6 +34,7 @@ export interface PackageInfo {
34
34
  bundledDependencies: string[];
35
35
  repositoryDirectory: string | undefined;
36
36
  ecosystem: Ecosystem;
37
+ main: string | undefined;
37
38
  exports: unknown;
38
39
  bin: Record<string, string>;
39
40
  files: string[];
@@ -49,6 +50,11 @@ export interface BinTarget {
49
50
  /** Owning package dir, e.g. "packages/superintendent". */
50
51
  dir: string;
51
52
  }
53
+ export interface RootEntryPoint {
54
+ kind: "main" | "export" | "bin";
55
+ name: string;
56
+ target: string;
57
+ }
52
58
  export interface ReleaseWorkflow {
53
59
  /** File name, e.g. "release-toolcraft.yml". */
54
60
  file: string;
@@ -75,6 +81,9 @@ export interface WorkspaceModel {
75
81
  binTargets: BinTarget[];
76
82
  /** Real import graph of each package's `src`, keyed by package dir. */
77
83
  sourceImports: SourceImportView;
84
+ /** Runtime imports of root package entrypoints that ship in the root tarball. */
85
+ shippedDistImports: SourceImportView;
86
+ rootEntryPoints: RootEntryPoint[];
78
87
  /** Runtime file assets discovered from package source, keyed by package dir. */
79
88
  runtimeFileAssets: RuntimeFileAssetView;
80
89
  /** Files included by each package artifact, keyed by package dir. */
@@ -2,7 +2,7 @@ import path from "node:path";
2
2
  import { parse as parseYaml } from "yaml";
3
3
  import { createNpmPacklistProvider, loadPackageFileView } from "./packlist.js";
4
4
  import { scanRuntimeFileAssets } from "./runtime-files.js";
5
- import { scanSourceImports } from "./source-imports.js";
5
+ import { scanImportFiles, scanSourceImports } from "./source-imports.js";
6
6
  function toPosix(p) {
7
7
  return p.replaceAll("\\", "/");
8
8
  }
@@ -100,6 +100,7 @@ async function loadPackage(fs, rootDir, relDir, isRoot) {
100
100
  bundledDependencies: toStringArray(pkg.bundledDependencies ?? pkg.bundleDependencies),
101
101
  repositoryDirectory: typeof repository?.directory === "string" ? repository.directory : undefined,
102
102
  ecosystem,
103
+ main: typeof pkg.main === "string" ? pkg.main : undefined,
103
104
  exports: pkg.exports,
104
105
  bin: toBinRecord(pkg.bin, typeof pkg.name === "string" ? pkg.name : relDir),
105
106
  files: Array.isArray(pkg.files)
@@ -133,6 +134,87 @@ function deriveShipped(root) {
133
134
  }
134
135
  return { shippedDirs, binTargets };
135
136
  }
137
+ function normalizeRootTarget(target) {
138
+ const withoutDot = target.startsWith("./") ? target.slice(2) : target;
139
+ const normalized = toPosix(path.posix.normalize(toPosix(withoutDot)));
140
+ if (normalized.length === 0 ||
141
+ normalized === "." ||
142
+ normalized === ".." ||
143
+ normalized.startsWith("../") ||
144
+ path.posix.isAbsolute(normalized)) {
145
+ return undefined;
146
+ }
147
+ return normalized;
148
+ }
149
+ function exportImportTarget(value) {
150
+ if (typeof value === "string")
151
+ return normalizeRootTarget(value);
152
+ if (Array.isArray(value)) {
153
+ for (const item of value) {
154
+ const target = exportImportTarget(item);
155
+ if (target)
156
+ return target;
157
+ }
158
+ return undefined;
159
+ }
160
+ if (!value || typeof value !== "object")
161
+ return undefined;
162
+ const record = value;
163
+ for (const [key, raw] of Object.entries(record)) {
164
+ if (key === "types")
165
+ continue;
166
+ const target = exportImportTarget(raw);
167
+ if (target)
168
+ return target;
169
+ }
170
+ return undefined;
171
+ }
172
+ function isSubpathExportsMap(exportsField) {
173
+ if (!exportsField || typeof exportsField !== "object" || Array.isArray(exportsField)) {
174
+ return false;
175
+ }
176
+ return Object.keys(exportsField).some((key) => key.startsWith("."));
177
+ }
178
+ function deriveRootEntryPoints(root) {
179
+ const entryPoints = [];
180
+ const seen = new Set();
181
+ const add = (entry) => {
182
+ const key = `${entry.kind}:${entry.name}:${entry.target}`;
183
+ if (seen.has(key))
184
+ return;
185
+ seen.add(key);
186
+ entryPoints.push(entry);
187
+ };
188
+ if (root.main) {
189
+ const target = normalizeRootTarget(root.main);
190
+ if (target)
191
+ add({ kind: "main", name: ".", target });
192
+ }
193
+ if (isSubpathExportsMap(root.exports)) {
194
+ for (const [name, value] of Object.entries(root.exports)) {
195
+ const target = exportImportTarget(value);
196
+ if (target)
197
+ add({ kind: "export", name, target });
198
+ }
199
+ }
200
+ else {
201
+ const target = exportImportTarget(root.exports);
202
+ if (target)
203
+ add({ kind: "export", name: ".", target });
204
+ }
205
+ for (const [name, value] of Object.entries(root.bin)) {
206
+ const target = normalizeRootTarget(value);
207
+ if (target)
208
+ add({ kind: "bin", name, target });
209
+ }
210
+ return entryPoints.sort((a, b) => {
211
+ const byTarget = a.target.localeCompare(b.target);
212
+ if (byTarget !== 0)
213
+ return byTarget;
214
+ const byKind = a.kind.localeCompare(b.kind);
215
+ return byKind !== 0 ? byKind : a.name.localeCompare(b.name);
216
+ });
217
+ }
136
218
  function dirFromArtifactPath(p) {
137
219
  const parts = toPosix(p).split("/").filter(Boolean);
138
220
  if (parts[parts.length - 1] === "dist")
@@ -274,6 +356,7 @@ export async function loadWorkspace(fs, rootDir, options = {}) {
274
356
  for (const p of packages)
275
357
  byDir.set(p.dir, p);
276
358
  const { shippedDirs, binTargets } = deriveShipped(root);
359
+ const rootEntryPoints = deriveRootEntryPoints(root);
277
360
  const workspaceNames = new Set(packages.map((p) => p.name));
278
361
  const sourceImportPackages = packages.map((pkg) => ({
279
362
  dir: pkg.dir,
@@ -284,8 +367,10 @@ export async function loadWorkspace(fs, rootDir, options = {}) {
284
367
  root,
285
368
  packages
286
369
  };
287
- const [sourceImports, runtimeFileAssets, packageFiles] = await Promise.all([
370
+ const shippedDistEntryFiles = [...new Set(rootEntryPoints.map((entry) => entry.target))];
371
+ const [sourceImports, shippedDistImports, runtimeFileAssets, packageFiles] = await Promise.all([
288
372
  scanSourceImports(fs, rootDir, sourceImportPackages),
373
+ scanImportFiles(fs, rootDir, shippedDistEntryFiles),
289
374
  scanRuntimeFileAssets(fs, rootDir, runtimePackages),
290
375
  loadPackageFileView(options.packlistProvider ?? createNpmPacklistProvider(fs), {
291
376
  rootDir,
@@ -302,6 +387,8 @@ export async function loadWorkspace(fs, rootDir, options = {}) {
302
387
  shippedDirs,
303
388
  binTargets,
304
389
  sourceImports,
390
+ shippedDistImports,
391
+ rootEntryPoints,
305
392
  runtimeFileAssets,
306
393
  packageFiles
307
394
  };
@@ -9,6 +9,15 @@ function shippedPackageNames(model) {
9
9
  }
10
10
  return names;
11
11
  }
12
+ function rootRuntimeDependencies(model) {
13
+ return new Set([
14
+ ...Object.keys(model.root.dependencies),
15
+ ...Object.keys(model.root.optionalDependencies)
16
+ ]);
17
+ }
18
+ function entryPointLabel(entry) {
19
+ return `${entry.kind}:${entry.name}`;
20
+ }
12
21
  /**
13
22
  * Every runtime dependency of a shipped, tsc-emitted bin entry must resolve
14
23
  * from the published `poe-code` tarball: it is in root `dependencies`, a Node
@@ -20,7 +29,31 @@ export const shippedDistDepsUnresolvable = {
20
29
  run(model) {
21
30
  const violations = [];
22
31
  const shipped = shippedPackageNames(model);
23
- const rootDeps = new Set(Object.keys(model.root.dependencies));
32
+ const rootDeps = rootRuntimeDependencies(model);
33
+ for (const entry of model.rootEntryPoints) {
34
+ const unresolved = new Set();
35
+ for (const ref of model.shippedDistImports.get(entry.target) ?? []) {
36
+ if (ref.kind !== "bare" || ref.typeOnly || !ref.packageName)
37
+ continue;
38
+ if (ref.packageName === model.root.name)
39
+ continue;
40
+ if (rootDeps.has(ref.packageName) || isBuiltin(ref.packageName))
41
+ continue;
42
+ unresolved.add(ref.packageName);
43
+ }
44
+ if (unresolved.size === 0)
45
+ continue;
46
+ const names = [...unresolved].sort();
47
+ violations.push({
48
+ rule: id,
49
+ package: model.root.name,
50
+ severity: "error",
51
+ via: entryPointLabel(entry),
52
+ detail: { target: entry.target, unresolved: names },
53
+ message: "root package entrypoint imports bare names not in root dependencies (ERR_MODULE_NOT_FOUND on install)",
54
+ fix: `Bundle or rewrite ${entry.target}, or add each package to root "dependencies": ${names.join(", ")}.`
55
+ });
56
+ }
24
57
  for (const bin of model.binTargets) {
25
58
  const owner = model.byDir.get(bin.dir);
26
59
  if (!owner)
@@ -17,7 +17,7 @@ export interface ImportRef {
17
17
  }
18
18
  /** Imports found in each workspace package, keyed by the package dir (posix). */
19
19
  export type SourceImportView = Map<string, ImportRef[]>;
20
- interface RawImport {
20
+ export interface RawImport {
21
21
  specifier: string;
22
22
  typeOnly: boolean;
23
23
  }
@@ -33,4 +33,4 @@ export declare function scanSourceImports(fs: LintFs, rootDir: string, packages:
33
33
  dir: string;
34
34
  workspaceNames: ReadonlySet<string>;
35
35
  }>): Promise<SourceImportView>;
36
- export {};
36
+ export declare function scanImportFiles(fs: LintFs, rootDir: string, files: string[]): Promise<SourceImportView>;
@@ -14,8 +14,18 @@ function isTestFile(relFile) {
14
14
  const segments = relFile.split("/");
15
15
  return segments.some((s) => s === "test" || s === "tests" || s === "__tests__");
16
16
  }
17
+ function scriptKind(fileName) {
18
+ if (fileName.endsWith(".tsx"))
19
+ return ts.ScriptKind.TSX;
20
+ if (fileName.endsWith(".jsx"))
21
+ return ts.ScriptKind.JSX;
22
+ if (fileName.endsWith(".js") || fileName.endsWith(".mjs") || fileName.endsWith(".cjs")) {
23
+ return ts.ScriptKind.JS;
24
+ }
25
+ return ts.ScriptKind.TS;
26
+ }
17
27
  function extractImportsFromAst(text, fileName) {
18
- const sourceFile = ts.createSourceFile(fileName, text, ts.ScriptTarget.Latest, false, fileName.endsWith(".tsx") ? ts.ScriptKind.TSX : ts.ScriptKind.TS);
28
+ const sourceFile = ts.createSourceFile(fileName, text, ts.ScriptTarget.Latest, false, scriptKind(fileName));
19
29
  const out = [];
20
30
  for (const statement of sourceFile.statements) {
21
31
  if (ts.isImportDeclaration(statement)) {
@@ -176,3 +186,20 @@ export async function scanSourceImports(fs, rootDir, packages) {
176
186
  }));
177
187
  return view;
178
188
  }
189
+ export async function scanImportFiles(fs, rootDir, files) {
190
+ const view = new Map();
191
+ await Promise.all(files.map(async (relFile) => {
192
+ const normalized = path.posix.normalize(toPosix(relFile));
193
+ const absFile = path.join(rootDir, normalized);
194
+ let text;
195
+ try {
196
+ text = await fs.readFile(absFile);
197
+ }
198
+ catch {
199
+ return;
200
+ }
201
+ const refs = extractRelevantImports(text, absFile).map((raw) => classify(raw, rootDir, ".", absFile, normalized));
202
+ view.set(normalized, refs);
203
+ }));
204
+ return view;
205
+ }