quilltap 3.1.0-dev.27 → 3.1.0-dev.30
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/bin/quilltap.js +118 -17
- package/package.json +1 -1
package/bin/quilltap.js
CHANGED
|
@@ -114,23 +114,49 @@ function openBrowser(url) {
|
|
|
114
114
|
});
|
|
115
115
|
}
|
|
116
116
|
|
|
117
|
+
// Resolve a native module's directory, handling npm hoisting.
|
|
118
|
+
// Returns the directory containing package.json, or null if not found.
|
|
119
|
+
function resolveModuleDir(moduleName) {
|
|
120
|
+
try {
|
|
121
|
+
const pkgJson = require.resolve(moduleName + '/package.json');
|
|
122
|
+
return path.dirname(pkgJson);
|
|
123
|
+
} catch {
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
117
128
|
// Check if native modules are compiled for the current Node.js version.
|
|
118
129
|
// This handles the case where npx caches the package but the user upgrades
|
|
119
130
|
// Node.js — the cached native modules will have a stale NODE_MODULE_VERSION.
|
|
120
131
|
function ensureNativeModules() {
|
|
121
|
-
const nativeModules = ['better-sqlite3', 'sharp'];
|
|
122
132
|
const needsRebuild = [];
|
|
123
133
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
+
// Check better-sqlite3: it lazy-loads the native .node binary only when you
|
|
135
|
+
// create a Database, so a bare require('better-sqlite3') always succeeds.
|
|
136
|
+
// We must load the native binding directly to detect NODE_MODULE_VERSION mismatches.
|
|
137
|
+
// Use require.resolve to find it regardless of npm hoisting.
|
|
138
|
+
try {
|
|
139
|
+
const modDir = resolveModuleDir('better-sqlite3');
|
|
140
|
+
if (!modDir) throw Object.assign(new Error('not found'), { code: 'MODULE_NOT_FOUND' });
|
|
141
|
+
const bindingsPath = path.join(modDir, 'build', 'Release', 'better_sqlite3.node');
|
|
142
|
+
require(bindingsPath);
|
|
143
|
+
} catch (err) {
|
|
144
|
+
if (err.message && err.message.includes('NODE_MODULE_VERSION')) {
|
|
145
|
+
needsRebuild.push('better-sqlite3');
|
|
146
|
+
} else if (err.code === 'MODULE_NOT_FOUND') {
|
|
147
|
+
needsRebuild.push('better-sqlite3');
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Check sharp: loads its native binding eagerly on require, but we use
|
|
152
|
+
// the same explicit-path approach for consistency and reliability.
|
|
153
|
+
try {
|
|
154
|
+
require('sharp');
|
|
155
|
+
} catch (err) {
|
|
156
|
+
if (err.message && err.message.includes('NODE_MODULE_VERSION')) {
|
|
157
|
+
needsRebuild.push('sharp');
|
|
158
|
+
} else if (err.code === 'MODULE_NOT_FOUND') {
|
|
159
|
+
needsRebuild.push('sharp');
|
|
134
160
|
}
|
|
135
161
|
}
|
|
136
162
|
|
|
@@ -153,6 +179,77 @@ function ensureNativeModules() {
|
|
|
153
179
|
}
|
|
154
180
|
}
|
|
155
181
|
|
|
182
|
+
// Symlink native modules into the standalone directory's node_modules
|
|
183
|
+
// so that standard Node.js resolution finds them without relying on NODE_PATH.
|
|
184
|
+
function linkNativeModules(standaloneDir) {
|
|
185
|
+
const standaloneNodeModules = path.join(standaloneDir, 'node_modules');
|
|
186
|
+
|
|
187
|
+
// Ensure top-level node_modules exists in standalone dir
|
|
188
|
+
if (!fs.existsSync(standaloneNodeModules)) {
|
|
189
|
+
fs.mkdirSync(standaloneNodeModules, { recursive: true });
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const symlinkType = process.platform === 'win32' ? 'junction' : 'dir';
|
|
193
|
+
|
|
194
|
+
// Link a single module directory into standaloneDir/node_modules/<name>
|
|
195
|
+
function linkModule(name, sourceDir) {
|
|
196
|
+
if (!sourceDir) return;
|
|
197
|
+
const targetPath = path.join(standaloneNodeModules, name);
|
|
198
|
+
|
|
199
|
+
// If already exists and points to the right place, skip
|
|
200
|
+
if (fs.existsSync(targetPath)) {
|
|
201
|
+
try {
|
|
202
|
+
const existing = fs.realpathSync(targetPath);
|
|
203
|
+
const source = fs.realpathSync(sourceDir);
|
|
204
|
+
if (existing === source) return; // already linked correctly
|
|
205
|
+
} catch {
|
|
206
|
+
// If we can't resolve, remove and re-link
|
|
207
|
+
}
|
|
208
|
+
// Remove stale link/dir
|
|
209
|
+
fs.rmSync(targetPath, { recursive: true, force: true });
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Ensure parent directory exists (for scoped packages like @img/sharp-*)
|
|
213
|
+
const parentDir = path.dirname(targetPath);
|
|
214
|
+
if (!fs.existsSync(parentDir)) {
|
|
215
|
+
fs.mkdirSync(parentDir, { recursive: true });
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
try {
|
|
219
|
+
fs.symlinkSync(sourceDir, targetPath, symlinkType);
|
|
220
|
+
} catch (err) {
|
|
221
|
+
// If symlink fails (e.g. permissions), try copying as fallback
|
|
222
|
+
console.error(` Warning: Could not symlink ${name}: ${err.message}`);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Link better-sqlite3
|
|
227
|
+
const betterSqlite3Dir = resolveModuleDir('better-sqlite3');
|
|
228
|
+
linkModule('better-sqlite3', betterSqlite3Dir);
|
|
229
|
+
|
|
230
|
+
// Link sharp
|
|
231
|
+
const sharpDir = resolveModuleDir('sharp');
|
|
232
|
+
linkModule('sharp', sharpDir);
|
|
233
|
+
|
|
234
|
+
// Link sharp's @img platform packages — they live near sharp's location
|
|
235
|
+
if (sharpDir) {
|
|
236
|
+
const sharpParent = path.dirname(sharpDir);
|
|
237
|
+
|
|
238
|
+
// If sharp is in a scoped dir or regular node_modules, look for @img there
|
|
239
|
+
const imgDir = path.join(sharpParent, '@img');
|
|
240
|
+
if (fs.existsSync(imgDir)) {
|
|
241
|
+
try {
|
|
242
|
+
const imgPackages = fs.readdirSync(imgDir).filter(name => name.startsWith('sharp-'));
|
|
243
|
+
for (const pkg of imgPackages) {
|
|
244
|
+
linkModule(`@img/${pkg}`, path.join(imgDir, pkg));
|
|
245
|
+
}
|
|
246
|
+
} catch {
|
|
247
|
+
// Non-fatal — sharp may work without explicit @img links
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
156
253
|
// Main
|
|
157
254
|
async function main() {
|
|
158
255
|
const opts = parseArgs(process.argv);
|
|
@@ -192,6 +289,9 @@ async function main() {
|
|
|
192
289
|
// Ensure native modules are compiled for the current Node.js version
|
|
193
290
|
ensureNativeModules();
|
|
194
291
|
|
|
292
|
+
// Symlink native modules into standalone dir so standard resolution finds them
|
|
293
|
+
linkNativeModules(standaloneDir);
|
|
294
|
+
|
|
195
295
|
// Set up environment
|
|
196
296
|
const env = {
|
|
197
297
|
...process.env,
|
|
@@ -204,13 +304,14 @@ async function main() {
|
|
|
204
304
|
env.QUILLTAP_DATA_DIR = path.resolve(opts.dataDir);
|
|
205
305
|
}
|
|
206
306
|
|
|
207
|
-
// Set NODE_PATH
|
|
208
|
-
//
|
|
209
|
-
//
|
|
307
|
+
// Set NODE_PATH as a fallback — native modules are symlinked into standaloneDir
|
|
308
|
+
// but NODE_PATH covers any other dependencies. Include the parent node_modules
|
|
309
|
+
// to handle npm hoisting (e.g. npx installs where deps are hoisted up a level).
|
|
210
310
|
const packageNodeModules = path.join(PACKAGE_DIR, 'node_modules');
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
311
|
+
const parentNodeModules = path.resolve(PACKAGE_DIR, '..');
|
|
312
|
+
env.NODE_PATH = [packageNodeModules, parentNodeModules, env.NODE_PATH]
|
|
313
|
+
.filter(Boolean)
|
|
314
|
+
.join(path.delimiter);
|
|
214
315
|
|
|
215
316
|
const url = `http://localhost:${opts.port}`;
|
|
216
317
|
|