@jointhedots/gear 1.1.14 → 1.1.16

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.
@@ -38,7 +38,7 @@ function flush() {
38
38
  flushing = false;
39
39
  }
40
40
 
41
- var scope = typeof globalThis !== "undefined" ? globalThis : (typeof self !== "undefined" ? self : window);
41
+ var scope = typeof globalThis !== "undefined" ? globalThis : (typeof self !== "undefined" ? self : globalThis);
42
42
  var BrowserMutationObserver = scope.MutationObserver || scope.WebKitMutationObserver;
43
43
 
44
44
  if (typeof BrowserMutationObserver === "function") {
@@ -227,7 +227,7 @@ const process = {
227
227
  config,
228
228
  uptime,
229
229
  }
230
- window.process = process
230
+ globalThis.process = process
231
231
 
232
232
  export {
233
233
  nextTick,
@@ -4,5 +4,214 @@
4
4
  import './process.js'
5
5
  import './buffer.js'
6
6
 
7
- // Make Buffer available globally
8
- globalThis.Buffer = Buffer
7
+ // Add Node.js-specific API to W3C ReadableStream
8
+ {
9
+ const proto = ReadableStream.prototype
10
+
11
+ // Start consuming the stream, emitting 'data'/'end'/'error'/'close' events
12
+ function _startReading(stream) {
13
+ if (stream._reading) return
14
+ stream._reading = true
15
+ stream._paused = false
16
+ const reader = stream.getReader()
17
+ const pump = () => reader.read().then(({ done, value }) => {
18
+ if (done) {
19
+ stream._reading = false
20
+ stream._emit('end')
21
+ stream._emit('close')
22
+ return
23
+ }
24
+ stream._emit('data', value)
25
+ if (!stream._paused) return pump()
26
+ stream._pump = pump
27
+ }).catch(err => {
28
+ stream._reading = false
29
+ stream._emit('error', err)
30
+ })
31
+ pump()
32
+ }
33
+
34
+ proto._emit = function (event, ...args) {
35
+ this._listeners?.[event]?.slice().forEach(fn => fn(...args))
36
+ }
37
+
38
+ proto.on = proto.addListener = function (event, fn) {
39
+ this._listeners ??= {}
40
+ ; (this._listeners[event] ??= []).push(fn)
41
+ if (event === 'data') _startReading(this)
42
+ return this
43
+ }
44
+
45
+ proto.once = function (event, fn) {
46
+ const wrapped = (...args) => { this.off(event, wrapped); fn(...args) }
47
+ return this.on(event, wrapped)
48
+ }
49
+
50
+ proto.off = proto.removeListener = function (event, fn) {
51
+ const list = this._listeners?.[event]
52
+ if (list) { const i = list.indexOf(fn); if (i >= 0) list.splice(i, 1) }
53
+ return this
54
+ }
55
+
56
+ proto.pipe = function (dest) {
57
+ this.on('data', chunk => dest.write?.(chunk))
58
+ this.once('end', () => dest.end?.())
59
+ this.once('error', err => dest.destroy?.(err))
60
+ return dest
61
+ }
62
+
63
+ proto.pause = function () {
64
+ this._paused = true
65
+ return this
66
+ }
67
+
68
+ proto.resume = function () {
69
+ this._paused = false
70
+ this._pump?.()
71
+ return this
72
+ }
73
+
74
+ proto.destroy = function (err) {
75
+ if (err) this._emit('error', err)
76
+ this._emit('close')
77
+ this.cancel(err).catch(() => { })
78
+ return this
79
+ }
80
+
81
+ proto.unpipe = function (dest) {
82
+ this._listeners = {}
83
+ return this
84
+ }
85
+ }
86
+
87
+ // Add Node.js-specific API to W3C WritableStream
88
+ {
89
+ const proto = WritableStream.prototype
90
+
91
+ proto._emit = function (event, ...args) {
92
+ this._listeners?.[event]?.slice().forEach(fn => fn(...args))
93
+ }
94
+
95
+ proto.on = proto.addListener = function (event, fn) {
96
+ this._listeners ??= {}
97
+ ; (this._listeners[event] ??= []).push(fn)
98
+ return this
99
+ }
100
+
101
+ proto.once = function (event, fn) {
102
+ const wrapped = (...args) => { this.off(event, wrapped); fn(...args) }
103
+ return this.on(event, wrapped)
104
+ }
105
+
106
+ proto.off = proto.removeListener = function (event, fn) {
107
+ const list = this._listeners?.[event]
108
+ if (list) { const i = list.indexOf(fn); if (i >= 0) list.splice(i, 1) }
109
+ return this
110
+ }
111
+
112
+ proto._getWriter = function () {
113
+ if (!this._writer) this._writer = this.getWriter()
114
+ return this._writer
115
+ }
116
+
117
+ proto.write = function (chunk, encoding, callback) {
118
+ if (typeof encoding === 'function') { callback = encoding; encoding = undefined }
119
+ const writer = this._getWriter()
120
+ writer.ready.then(() => writer.write(chunk)).then(
121
+ () => { this._emit('drain'); callback?.() },
122
+ err => { this._emit('error', err); callback?.(err) }
123
+ )
124
+ return true
125
+ }
126
+
127
+ proto.end = function (chunk, encoding, callback) {
128
+ if (typeof chunk === 'function') { callback = chunk; chunk = null }
129
+ else if (typeof encoding === 'function') { callback = encoding; encoding = null }
130
+ const writer = this._getWriter()
131
+ const finish = () => writer.close().then(
132
+ () => { this._emit('finish'); this._emit('close'); callback?.() },
133
+ err => { this._emit('error', err); callback?.(err) }
134
+ )
135
+ if (chunk != null) {
136
+ writer.ready.then(() => writer.write(chunk)).then(finish, finish)
137
+ } else {
138
+ finish()
139
+ }
140
+ return this
141
+ }
142
+
143
+ proto.destroy = function (err) {
144
+ const writer = this._getWriter()
145
+ if (err) {
146
+ writer.abort(err).catch(() => { })
147
+ this._emit('error', err)
148
+ } else {
149
+ writer.close().catch(() => { })
150
+ }
151
+ this._emit('close')
152
+ return this
153
+ }
154
+
155
+ proto.cork = function () { this._corked = (this._corked || 0) + 1 }
156
+ proto.uncork = function () { if (this._corked > 0) this._corked-- }
157
+
158
+ proto.setDefaultEncoding = function (encoding) {
159
+ this._defaultEncoding = encoding
160
+ return this
161
+ }
162
+ }
163
+
164
+ // Add Node.js-specific API to W3C TransformStream
165
+ {
166
+ const proto = TransformStream.prototype
167
+
168
+ proto._emit = function (event, ...args) {
169
+ this._listeners?.[event]?.slice().forEach(fn => fn(...args))
170
+ }
171
+
172
+ proto.on = proto.addListener = function (event, fn) {
173
+ this._listeners ??= {}
174
+ ; (this._listeners[event] ??= []).push(fn)
175
+ if (event === 'data') this.readable.on('data', chunk => this._emit('data', chunk))
176
+ return this
177
+ }
178
+
179
+ proto.once = function (event, fn) {
180
+ const wrapped = (...args) => { this.off(event, wrapped); fn(...args) }
181
+ return this.on(event, wrapped)
182
+ }
183
+
184
+ proto.off = proto.removeListener = function (event, fn) {
185
+ const list = this._listeners?.[event]
186
+ if (list) { const i = list.indexOf(fn); if (i >= 0) list.splice(i, 1) }
187
+ return this
188
+ }
189
+
190
+ proto.write = function (chunk, encoding, callback) {
191
+ return this.writable.write(chunk, encoding, callback)
192
+ }
193
+
194
+ proto.end = function (chunk, encoding, callback) {
195
+ return this.writable.end(chunk, encoding, callback)
196
+ }
197
+
198
+ proto.pipe = function (dest) {
199
+ return this.readable.pipe(dest)
200
+ }
201
+
202
+ proto.pause = function () {
203
+ this.readable.pause()
204
+ return this
205
+ }
206
+
207
+ proto.resume = function () {
208
+ this.readable.resume()
209
+ return this
210
+ }
211
+
212
+ proto.destroy = function (err) {
213
+ this.readable.destroy(err)
214
+ this.writable.destroy(err)
215
+ return this
216
+ }
217
+ }
@@ -21,7 +21,7 @@ function getEsbuildLogLevel(mode) {
21
21
  return "silent";
22
22
  }
23
23
  }
24
- export async function create_esbuild_context(task, devmode) {
24
+ async function create_esbuild_context(task, devmode, onReady) {
25
25
  const ws = task.target.workspace;
26
26
  // Define modules mapping - using @jspm/core polyfills (same as Vite)
27
27
  const jspmPolyfills = Path.resolve(PackageRootDir, "node_modules/@jspm/core/nodelibs/browser");
@@ -111,7 +111,7 @@ export async function create_esbuild_context(task, devmode) {
111
111
  ESModuleResolverPlugin(modules_mapping, tsconfig),
112
112
  ...task.plugins,
113
113
  StyleSheetPlugin(task),
114
- StoragePlugin(task),
114
+ StoragePlugin(task, onReady),
115
115
  ],
116
116
  loader: {
117
117
  '.jpg': 'file',
@@ -255,7 +255,7 @@ function hasTailwindConfig(workspacePath) {
255
255
  catch { }
256
256
  return false;
257
257
  }
258
- export function StoragePlugin(task) {
258
+ export function StoragePlugin(task, onReady) {
259
259
  return {
260
260
  name: "dipatch-files",
261
261
  setup: (build) => {
@@ -290,7 +290,11 @@ export function StoragePlugin(task) {
290
290
  task.target.store();
291
291
  const storeTime = (Date.now() - storeStart) / 1000;
292
292
  const buildTime = (Date.now() - buildStartTime) / 1000;
293
- task.log.info(`Build ES modules in ${result.outputFiles.length} file(s) (compile: ${buildTime.toFixed(2)}s, store: ${storeTime.toFixed(2)}s)`);
293
+ task.log.info(`Store ES graph in ${result.outputFiles.length} file(s) (compile: ${buildTime.toFixed(2)}s, store: ${storeTime.toFixed(2)}s)`);
294
+ }
295
+ if (onReady) {
296
+ onReady();
297
+ onReady = null;
294
298
  }
295
299
  return null;
296
300
  });
@@ -602,11 +606,14 @@ export class ESModulesTask extends BuildTask {
602
606
  await prev_ctx.dispose();
603
607
  }
604
608
  const { target } = this;
605
- this.context = await create_esbuild_context(this, target.devmode);
606
609
  if (target.watch) {
607
- await this.context.watch();
610
+ return new Promise(async (resolve, reject) => {
611
+ this.context = await create_esbuild_context(this, target.devmode, resolve);
612
+ await this.context.watch();
613
+ });
608
614
  }
609
615
  else {
616
+ this.context = await create_esbuild_context(this, target.devmode);
610
617
  await this.context.rebuild();
611
618
  await this.context.dispose();
612
619
  }
@@ -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.16",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "jointhedots-gear": "esm/cli.js"