@jointhedots/gear 1.1.14 → 1.1.15

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.
@@ -2,7 +2,8 @@ import Fs from 'fs';
2
2
  import Os from 'os';
3
3
  import Path from 'path';
4
4
  import Ts from 'typescript';
5
- import { execFileSync } from 'child_process';
5
+ import { execFile } from 'child_process';
6
+ import { promisify } from 'util';
6
7
  import { BuildTask } from "./task.js";
7
8
  import { BuildTarget } from "../build-target.js";
8
9
  import { Library } from "../../model/workspace.js";
@@ -93,7 +94,7 @@ class TypescriptProject {
93
94
  this.dtsDir = Fs.mkdtempSync(Path.join(Os.tmpdir(), 'gear-dts-'));
94
95
  }
95
96
  /** Run tsgo to emit .d.ts files into dtsDir. Returns stderr output. */
96
- emitWithTsgo() {
97
+ async emitWithTsgo() {
97
98
  const tsgoPath = Path.resolve('node_modules/.bin/tsgo');
98
99
  const args = [
99
100
  '-p', Path.join(this.baseDir, 'tsconfig.json'),
@@ -103,17 +104,14 @@ class TypescriptProject {
103
104
  '--outDir', this.dtsDir,
104
105
  ];
105
106
  try {
106
- execFileSync(tsgoPath, args, {
107
+ await promisify(execFile)(tsgoPath, args, {
107
108
  cwd: this.baseDir,
108
- stdio: ['pipe', 'pipe', 'pipe'],
109
109
  timeout: 120_000,
110
110
  shell: true,
111
111
  });
112
- return '';
113
112
  }
114
113
  catch (e) {
115
114
  // tsgo may exit non-zero on type errors but still emit .d.ts files
116
- return e.stderr?.toString() || e.message || '';
117
115
  }
118
116
  }
119
117
  /** Recursively collect all .d.ts files emitted by tsgo */
@@ -144,30 +142,11 @@ export function createTypescriptProject(baseDir, tsconfig, outDir) {
144
142
  return new TypescriptProject(baseDir, tsconfig, outDir);
145
143
  }
146
144
  export function generateTypescriptDefinition(options) {
147
- const project = options.project;
145
+ const { project, prefix } = options;
148
146
  const baseDir = project.baseDir;
149
- const dtsDir = project.dtsDir;
150
- function isExcludedPath(fileName) {
151
- return fileName.includes('/node_modules/') || fileName.includes('\\node_modules\\');
152
- }
153
- // Compute baseUrl prefix to strip from module paths
154
147
  const normalizedBaseDir = normalizeFileName(Path.resolve(baseDir)) + "/";
155
- const normalizedDtsDir = normalizeFileName(Path.resolve(dtsDir)) + "/";
156
- let baseUrlPrefix = '';
157
- if (project.compilerOptions.baseUrl) {
158
- const absoluteBaseUrl = Path.resolve(baseDir, project.compilerOptions.baseUrl);
159
- const normalizedBaseUrl = normalizeFileName(absoluteBaseUrl);
160
- baseUrlPrefix = normalizedBaseUrl.slice(normalizedBaseDir.length);
161
- if (baseUrlPrefix && !baseUrlPrefix.endsWith('/')) {
162
- baseUrlPrefix += '/';
163
- }
164
- }
165
- function stripBaseUrlPrefix(modulePath) {
166
- if (baseUrlPrefix && modulePath.startsWith(baseUrlPrefix)) {
167
- return modulePath.slice(baseUrlPrefix.length);
168
- }
169
- return modulePath;
170
- }
148
+ const normalizedDtsDir = normalizeFileName(Path.resolve(project.dtsDir)) + "/";
149
+ const resolveCache = new Map();
171
150
  const outputParts = [];
172
151
  if (options.externs) {
173
152
  options.externs.forEach(function (path) {
@@ -179,8 +158,7 @@ export function generateTypescriptDefinition(options) {
179
158
  outputParts.push(`/// <reference types="${type}" />` + eol);
180
159
  });
181
160
  }
182
- // Map source-relative paths to internal: module IDs for ALL emitted files
183
- const internalsMap = {};
161
+ // Process each emitted .d.ts file as internal: module
184
162
  const dtsFiles = project.getEmittedDtsFiles();
185
163
  for (const dtsPath of dtsFiles) {
186
164
  const normalizedDts = normalizeFileName(Path.resolve(dtsPath));
@@ -188,83 +166,55 @@ export function generateTypescriptDefinition(options) {
188
166
  continue;
189
167
  const relativeDts = normalizedDts.slice(normalizedDtsDir.length);
190
168
  const relativeSource = relativeDts.replace(/\.d\.ts$/, '');
191
- const strippedSource = stripBaseUrlPrefix(relativeSource);
192
- const moduleId = `${options.prefix}:${strippedSource}`;
193
- internalsMap[relativeSource] = moduleId;
194
- internalsMap[strippedSource] = moduleId;
195
- if (strippedSource.endsWith('/index')) {
196
- internalsMap[strippedSource.slice(0, -6)] = moduleId;
197
- }
169
+ const data = Fs.readFileSync(dtsPath, 'utf-8');
170
+ const declFile = Ts.createSourceFile(dtsPath, data, Ts.ScriptTarget.Latest, true);
171
+ writeDeclaration(declFile, relativeSource);
198
172
  }
199
- // Build public export map: maps export entry ID to internal module ID
200
- const publicReexports = [];
173
+ // Emit public re-export modules
201
174
  if (options.exports) {
202
175
  for (const entry of Object.values(options.exports)) {
203
- const fileName = entry.source;
204
- if (isExcludedPath(fileName))
205
- continue;
206
- const normalizedSource = normalizeFileName(Path.resolve(fileName));
207
- if (!normalizedSource.startsWith(normalizedBaseDir))
208
- continue;
209
- const shortName = normalizedSource.slice(normalizedBaseDir.length);
210
- const shortNameNoExt = shortName.replace(/\.(ts|tsx|js|jsx)$/, '');
211
- const strippedShortNameNoExt = stripBaseUrlPrefix(shortNameNoExt);
212
- const internalId = internalsMap[shortNameNoExt] ||
213
- internalsMap[strippedShortNameNoExt] ||
214
- internalsMap[shortName];
176
+ const internalId = resolveInternalModuleImport(entry.source);
215
177
  if (internalId) {
216
- publicReexports.push({ id: entry.id, internalId });
178
+ outputParts.push(`declare module '${entry.id}' {${eol}`);
179
+ outputParts.push(`${indent}export * from '${internalId}';${eol}`);
180
+ outputParts.push(`}${eol}${eol}`);
217
181
  }
218
182
  }
219
183
  }
220
- // Unified module ID normalization
221
- function normalizeModuleId(moduleId) {
222
- moduleId = moduleId.replace(/\.(d\.ts|ts|tsx|js|jsx)$/, '');
223
- moduleId = stripBaseUrlPrefix(moduleId);
224
- const remapped = internalsMap[moduleId] ||
225
- internalsMap[moduleId + "/index"] ||
226
- internalsMap[moduleId + "/index.ts"];
227
- return remapped || moduleId;
228
- }
229
- function isExternalModule(moduleId) {
230
- return !internalsMap[moduleId];
231
- }
232
- // Process each emitted .d.ts file as internal: module
233
- for (const dtsPath of dtsFiles) {
234
- const normalizedDts = normalizeFileName(Path.resolve(dtsPath));
235
- if (!normalizedDts.startsWith(normalizedDtsDir))
236
- continue;
237
- const relativeDts = normalizedDts.slice(normalizedDtsDir.length);
238
- const relativeSource = relativeDts.replace(/\.d\.ts$/, '');
239
- const data = Fs.readFileSync(dtsPath, 'utf-8');
240
- const declFile = Ts.createSourceFile(dtsPath, data, Ts.ScriptTarget.Latest, true);
241
- writeDeclaration(declFile, relativeSource);
242
- }
243
- // Emit public re-export modules
244
- for (const { id, internalId } of publicReexports) {
245
- outputParts.push(`declare module '${id}' {${eol}`);
246
- outputParts.push(`${indent}export * from '${internalId}';${eol}`);
247
- outputParts.push(`}${eol}${eol}`);
184
+ function resolveInternalModuleImport(moduleId, importDir = "") {
185
+ if (moduleId.charAt(0) === '.') {
186
+ return prefix + normalizeFileName(Path.join(importDir, moduleId));
187
+ }
188
+ if (resolveCache.has(moduleId))
189
+ return resolveCache.get(moduleId);
190
+ const containingFile = Path.resolve(baseDir, importDir, '__resolve.ts');
191
+ const result = Ts.resolveModuleName(moduleId, containingFile, project.compilerOptions, Ts.sys);
192
+ if (result.resolvedModule && !result.resolvedModule.isExternalLibraryImport) {
193
+ const resolved = normalizeFileName(result.resolvedModule.resolvedFileName);
194
+ if (resolved.startsWith(normalizedBaseDir)) {
195
+ const value = prefix + resolved.slice(normalizedBaseDir.length).replace(/\.(d\.ts|tsx?|jsx?)$/, '');
196
+ resolveCache.set(moduleId, value);
197
+ return value;
198
+ }
199
+ }
200
+ // Check if moduleId is already an internal path relative to baseDir
201
+ const absolute = normalizeFileName(Path.resolve(baseDir, importDir, moduleId));
202
+ if (absolute.startsWith(normalizedBaseDir)) {
203
+ const value = prefix + absolute.slice(normalizedBaseDir.length);
204
+ resolveCache.set(moduleId, value);
205
+ return value;
206
+ }
207
+ resolveCache.set(moduleId, null);
208
+ return null;
248
209
  }
249
210
  function writeDeclaration(declarationFile, rawSourceModuleId) {
250
- const moduleDeclId = normalizeModuleId(rawSourceModuleId);
211
+ const moduleDeclId = resolveInternalModuleImport(rawSourceModuleId);
212
+ const moduleDir = Path.dirname(rawSourceModuleId);
251
213
  outputParts.push(`declare module '${moduleDeclId}' {${eol}${indent}`);
252
- function resolveModuleImport(moduleId) {
253
- let resolved;
254
- if (moduleId.charAt(0) === '.') {
255
- resolved = normalizeFileName(Path.join(Path.dirname(rawSourceModuleId), moduleId));
256
- }
257
- else {
258
- resolved = moduleId;
259
- if (isExternalModule(resolved))
260
- return resolved;
261
- }
262
- return normalizeModuleId(resolved);
263
- }
264
214
  const content = processTree(declarationFile, function (node) {
265
215
  if (isNodeKindExternalModuleReference(node)) {
266
216
  const expression = node.expression;
267
- const resolved = resolveModuleImport(expression.text);
217
+ const resolved = resolveInternalModuleImport(expression.text, moduleDir) ?? expression.text;
268
218
  return ` require('${resolved}')`;
269
219
  }
270
220
  else if (isNodeKindImportDeclaration(node) && !node.importClause) {
@@ -275,8 +225,8 @@ export function generateTypescriptDefinition(options) {
275
225
  }
276
226
  else if (isNodeKindStringLiteral(node) && node.parent &&
277
227
  (isNodeKindExportDeclaration(node.parent) || isNodeKindImportDeclaration(node.parent) || isNodeKindImportType(node.parent?.parent))) {
278
- const resolved = resolveModuleImport(node.text);
279
- if (resolved) {
228
+ const resolved = resolveInternalModuleImport(node.text, moduleDir);
229
+ if (resolved != null) {
280
230
  return ` '${resolved}'`;
281
231
  }
282
232
  }
@@ -304,13 +254,10 @@ export class TypescriptDefinitionTask extends BuildTask {
304
254
  const configText = file.read.text(Path.join(lib.path, "./tsconfig.json"))
305
255
  || file.read.text("./tsconfig.json");
306
256
  const project = createTypescriptProject(lib.path, configText, storage.getBaseDirFS());
307
- const stderr = project.emitWithTsgo();
308
- if (stderr) {
309
- this.log.warn(stderr);
310
- }
257
+ await project.emitWithTsgo();
311
258
  const { dts } = generateTypescriptDefinition({
312
259
  project,
313
- prefix: lib.name,
260
+ prefix: lib.name + ":",
314
261
  exclude: ["node_modules/**/*"],
315
262
  exports: create_export_map(lib, lib.bundle),
316
263
  });
@@ -7,6 +7,7 @@ import { create_bundle_manifest } from "./create-manifests.js";
7
7
  import { Bundle, Library, Workspace } from "../workspace.js";
8
8
  import { file, make_canonical_path, make_normalized_dirname, make_normalized_path, make_relative_path } from "../../utils/file.js";
9
9
  import { findConfigFile, is_config_filename, readConfigFile, readSingletonConfigFile } from "./config-loader.js";
10
+ import { read_lockfile } from "./lockfile.js";
10
11
  const exclude_dirs = ["node_modules", ".git"];
11
12
  function is_ignored_dir(ws, path) {
12
13
  const normalized_path = make_normalized_path(path);
@@ -252,7 +253,7 @@ function show_constants(ws) {
252
253
  }
253
254
  }
254
255
  export async function discover_workspace(ws) {
255
- let package_lock = null;
256
+ let lockfile = null;
256
257
  for (let path = ws.path;;) {
257
258
  const package_json = await readJsonFile(path + "/package.json");
258
259
  if (package_json) {
@@ -267,30 +268,20 @@ export async function discover_workspace(ws) {
267
268
  };
268
269
  }
269
270
  }
270
- const package_lock_path = path + "/package-lock.json";
271
- if (!package_lock && Fs.existsSync(package_lock_path)) {
272
- package_lock = await readJsonFile(package_lock_path);
271
+ if (!lockfile) {
272
+ lockfile = await read_lockfile(path);
273
273
  }
274
274
  const next_path = make_normalized_dirname(path);
275
275
  if (next_path === path)
276
276
  break;
277
277
  path = next_path;
278
278
  }
279
- if (!package_lock) {
280
- throw new Error(`Package lock not found for '${ws.name}'`);
281
- }
282
- for (const location in package_lock.packages) {
283
- let pkg = package_lock.packages[location];
284
- if (location.startsWith("node_modules/")) {
285
- if (pkg.link) {
286
- pkg = package_lock.packages[pkg.resolved];
287
- }
288
- const name = location.replace(/^.*node_modules\//, "");
289
- ws.resolved_versions[name] = pkg.version;
290
- }
279
+ if (!lockfile) {
280
+ throw new Error(`Lock file not found for '${ws.name}'`);
291
281
  }
282
+ ws.resolved_versions = lockfile.resolved_versions;
292
283
  await discover_workspace_libraries(ws);
293
- for (const location in package_lock.packages) {
284
+ for (const location of lockfile.installed_locations) {
294
285
  await discover_library(ws, location, true);
295
286
  }
296
287
  for (const bun of ws.bundles) {
@@ -0,0 +1,108 @@
1
+ import Fs from "node:fs";
2
+ import Fsp from "node:fs/promises";
3
+ import YAML from "yaml";
4
+ import { readJsonFile } from "../storage.js";
5
+ export async function read_lockfile(dir) {
6
+ const npm_path = dir + "/package-lock.json";
7
+ if (Fs.existsSync(npm_path)) {
8
+ return parse_npm_lockfile(await readJsonFile(npm_path));
9
+ }
10
+ const pnpm_path = dir + "/pnpm-lock.yaml";
11
+ if (Fs.existsSync(pnpm_path)) {
12
+ return parse_pnpm_lockfile(YAML.parse(await Fsp.readFile(pnpm_path, "utf-8")));
13
+ }
14
+ const yarn_path = dir + "/yarn.lock";
15
+ if (Fs.existsSync(yarn_path)) {
16
+ return parse_yarn_lockfile(await Fsp.readFile(yarn_path, "utf-8"));
17
+ }
18
+ return null;
19
+ }
20
+ function parse_npm_lockfile(lock) {
21
+ const resolved_versions = {};
22
+ const installed_locations = [];
23
+ for (const location in lock.packages) {
24
+ if (!location)
25
+ continue;
26
+ installed_locations.push(location);
27
+ if (location.startsWith("node_modules/")) {
28
+ let pkg = lock.packages[location];
29
+ if (pkg.link) {
30
+ pkg = lock.packages[pkg.resolved];
31
+ }
32
+ const name = location.replace(/^.*node_modules\//, "");
33
+ if (pkg?.version) {
34
+ resolved_versions[name] = pkg.version;
35
+ }
36
+ }
37
+ }
38
+ return { resolved_versions, installed_locations };
39
+ }
40
+ function parse_pnpm_lockfile(lock) {
41
+ const resolved_versions = {};
42
+ const locations = new Set();
43
+ if (lock.packages) {
44
+ for (const key in lock.packages) {
45
+ const clean = key.startsWith("/") ? key.slice(1) : key;
46
+ const atIdx = clean.lastIndexOf("@");
47
+ if (atIdx > 0) {
48
+ const name = clean.slice(0, atIdx);
49
+ const version = clean.slice(atIdx + 1).split("(")[0];
50
+ if (name && version) {
51
+ resolved_versions[name] = version;
52
+ locations.add("node_modules/" + name);
53
+ }
54
+ }
55
+ }
56
+ }
57
+ if (lock.importers) {
58
+ for (const key in lock.importers) {
59
+ if (key !== ".")
60
+ locations.add(key);
61
+ }
62
+ }
63
+ return { resolved_versions, installed_locations: [...locations] };
64
+ }
65
+ function parse_yarn_lockfile(text) {
66
+ const resolved_versions = {};
67
+ const locations = new Set();
68
+ if (text.includes("__metadata:")) {
69
+ const lock = YAML.parse(text);
70
+ for (const key in lock) {
71
+ if (key === "__metadata")
72
+ continue;
73
+ if (lock[key]?.version) {
74
+ const name = extract_package_name(key.split(",")[0].trim());
75
+ if (name) {
76
+ resolved_versions[name] = lock[key].version;
77
+ locations.add("node_modules/" + name);
78
+ }
79
+ }
80
+ }
81
+ }
82
+ else {
83
+ parse_yarn_classic(text, resolved_versions, locations);
84
+ }
85
+ return { resolved_versions, installed_locations: [...locations] };
86
+ }
87
+ function extract_package_name(entry) {
88
+ entry = entry.replace(/^"|"$/g, "");
89
+ // @scope/name@version or name@version
90
+ const at = entry.startsWith("@") ? entry.indexOf("@", 1) : entry.indexOf("@");
91
+ return at > 0 ? entry.slice(0, at) : null;
92
+ }
93
+ function parse_yarn_classic(text, resolved_versions, locations) {
94
+ let current_name = null;
95
+ for (const line of text.split("\n")) {
96
+ const trimmed = line.trim();
97
+ if (!line.startsWith(" ") && !line.startsWith("#") && trimmed.endsWith(":")) {
98
+ current_name = extract_package_name(trimmed.slice(0, -1).split(",")[0].trim());
99
+ }
100
+ else if (current_name && trimmed.startsWith("version ")) {
101
+ const version = trimmed.slice(8).replace(/^"|"$/g, "");
102
+ resolved_versions[current_name] = version;
103
+ locations.add("node_modules/" + current_name);
104
+ current_name = null;
105
+ }
106
+ }
107
+ }
108
+ //# sourceMappingURL=lockfile.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jointhedots/gear",
3
- "version": "1.1.14",
3
+ "version": "1.1.15",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "jointhedots-gear": "esm/cli.js"