@ahmednawaz/crank 0.2.5 → 0.2.6
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/lib/init.js +144 -50
- package/package.json +4 -3
package/lib/init.js
CHANGED
|
@@ -187,6 +187,98 @@ module.exports = {
|
|
|
187
187
|
return stubPath;
|
|
188
188
|
}
|
|
189
189
|
|
|
190
|
+
const NODE_IMPORT_SPECS = [
|
|
191
|
+
{ re: /import\s+\{\s*createRequire\s*\}\s+from\s+['"]node:module['"]\s*;?/, line: "import { createRequire } from 'node:module'" },
|
|
192
|
+
{ re: /import\s+\{\s*existsSync\s*\}\s+from\s+['"]node:fs['"]\s*;?/, line: "import { existsSync } from 'node:fs'" },
|
|
193
|
+
{ re: /import\s+\{\s*fileURLToPath\s*\}\s+from\s+['"]node:url['"]\s*;?/, line: "import { fileURLToPath } from 'node:url'" }
|
|
194
|
+
];
|
|
195
|
+
|
|
196
|
+
function hasCrankVitePatch(source) {
|
|
197
|
+
return (
|
|
198
|
+
/_crankPluginPath/.test(source) ||
|
|
199
|
+
/vite-plugin\.cjs/.test(source) ||
|
|
200
|
+
/Crank Vite plugin/.test(source) ||
|
|
201
|
+
(/\bcrank\s*\?\s*\[\s*crank\s*\(\s*\)\s*\]/.test(source) && /plugins/.test(source)) ||
|
|
202
|
+
(/(\bcrank)\s*\(/.test(source) && /plugins/.test(source) && /\.crank\//.test(source))
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/** Drop later duplicate ESM imports of the Node builtins Crank injects. */
|
|
207
|
+
function dedupeCrankNodeImports(source) {
|
|
208
|
+
let next = source;
|
|
209
|
+
let changed = false;
|
|
210
|
+
for (const { re } of NODE_IMPORT_SPECS) {
|
|
211
|
+
const globalRe = new RegExp(re.source, 'g');
|
|
212
|
+
const matches = next.match(globalRe);
|
|
213
|
+
if (!matches || matches.length < 2) continue;
|
|
214
|
+
let seen = 0;
|
|
215
|
+
next = next.replace(globalRe, (m) => {
|
|
216
|
+
seen += 1;
|
|
217
|
+
if (seen === 1) return m;
|
|
218
|
+
changed = true;
|
|
219
|
+
return '';
|
|
220
|
+
});
|
|
221
|
+
// Clean blank lines left by removals (keep file readable)
|
|
222
|
+
next = next.replace(/\n{3,}/g, '\n\n');
|
|
223
|
+
}
|
|
224
|
+
return { source: next, changed };
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function missingNodeImportLines(source) {
|
|
228
|
+
return NODE_IMPORT_SPECS.filter(({ re }) => !re.test(source)).map(({ line }) => line);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function ensureCrankInPlugins(source) {
|
|
232
|
+
if (/\bcrank\s*\?\s*\[\s*crank\s*\(\s*\)\s*\]/.test(source) || /\[\s*\.\.\.\s*\(\s*crank\s*\?/.test(source)) {
|
|
233
|
+
return { source, changed: false, ok: true };
|
|
234
|
+
}
|
|
235
|
+
if (/plugins\s*:\s*\[\s*\]/.test(source)) {
|
|
236
|
+
return {
|
|
237
|
+
source: source.replace(/plugins\s*:\s*\[\s*\]/, 'plugins: [...(crank ? [crank()] : [])]'),
|
|
238
|
+
changed: true,
|
|
239
|
+
ok: true
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
if (/plugins\s*:\s*\[/.test(source)) {
|
|
243
|
+
return {
|
|
244
|
+
source: source.replace(/plugins\s*:\s*\[/, 'plugins: [...(crank ? [crank()] : []), '),
|
|
245
|
+
changed: true,
|
|
246
|
+
ok: true
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
if (/defineConfig\s*\(\s*\{/.test(source)) {
|
|
250
|
+
return {
|
|
251
|
+
source: source.replace(
|
|
252
|
+
/defineConfig\s*\(\s*\{/,
|
|
253
|
+
'defineConfig({\n plugins: [...(crank ? [crank()] : [])],'
|
|
254
|
+
),
|
|
255
|
+
changed: true,
|
|
256
|
+
ok: true
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
if (/export\s+default\s+\{/.test(source)) {
|
|
260
|
+
return {
|
|
261
|
+
source: source.replace(
|
|
262
|
+
/export\s+default\s+\{/,
|
|
263
|
+
'export default {\n plugins: [...(crank ? [crank()] : [])],'
|
|
264
|
+
),
|
|
265
|
+
changed: true,
|
|
266
|
+
ok: true
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
if (/module\.exports\s*=\s*\{/.test(source)) {
|
|
270
|
+
return {
|
|
271
|
+
source: source.replace(
|
|
272
|
+
/module\.exports\s*=\s*\{/,
|
|
273
|
+
'module.exports = {\n plugins: [...(crank ? [crank()] : [])],'
|
|
274
|
+
),
|
|
275
|
+
changed: true,
|
|
276
|
+
ok: true
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
return { source, changed: false, ok: false };
|
|
280
|
+
}
|
|
281
|
+
|
|
190
282
|
function patchViteConfig(projectRoot) {
|
|
191
283
|
const configPath = findViteConfig(projectRoot);
|
|
192
284
|
const stubPath = writeVitePluginStub(projectRoot);
|
|
@@ -196,11 +288,21 @@ function patchViteConfig(projectRoot) {
|
|
|
196
288
|
}
|
|
197
289
|
|
|
198
290
|
let source = fs.readFileSync(configPath, 'utf8');
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
) {
|
|
203
|
-
|
|
291
|
+
|
|
292
|
+
// Heal configs broken by older Crank versions (duplicate node: imports).
|
|
293
|
+
const repaired = dedupeCrankNodeImports(source);
|
|
294
|
+
if (repaired.changed) {
|
|
295
|
+
source = repaired.source;
|
|
296
|
+
fs.writeFileSync(configPath, source);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
if (hasCrankVitePatch(source)) {
|
|
300
|
+
return {
|
|
301
|
+
patched: repaired.changed,
|
|
302
|
+
reason: repaired.changed ? 'repaired-duplicate-imports' : 'already-present',
|
|
303
|
+
file: configPath,
|
|
304
|
+
plugin: stubPath
|
|
305
|
+
};
|
|
204
306
|
}
|
|
205
307
|
|
|
206
308
|
let relStub = path.relative(path.dirname(configPath), stubPath).replace(/\\/g, '/');
|
|
@@ -218,11 +320,8 @@ function patchViteConfig(projectRoot) {
|
|
|
218
320
|
const crankDecl = isTs
|
|
219
321
|
? 'let crank: null | (() => unknown) = null'
|
|
220
322
|
: 'let crank = null';
|
|
221
|
-
const
|
|
222
|
-
|
|
223
|
-
import { fileURLToPath } from 'node:url'
|
|
224
|
-
|
|
225
|
-
// Crank Vite plugin — graceful load (same pattern as Vizpatch); skip if unavailable
|
|
323
|
+
const importLines = missingNodeImportLines(source);
|
|
324
|
+
const gracefulBlock = `${importLines.length ? `${importLines.join('\n')}\n\n` : ''}// Crank Vite plugin — graceful load; skip if unavailable
|
|
226
325
|
${crankDecl}
|
|
227
326
|
try {
|
|
228
327
|
const _crankPluginPath = fileURLToPath(new URL('${relStub}', import.meta.url))
|
|
@@ -233,31 +332,14 @@ try {
|
|
|
233
332
|
} catch {}
|
|
234
333
|
|
|
235
334
|
`;
|
|
335
|
+
// If the file already uses createRequire/existsSync/fileURLToPath without
|
|
336
|
+
// importing them, missingNodeImportLines still adds the needed imports once.
|
|
236
337
|
if (!/_crankPluginPath/.test(source)) {
|
|
237
338
|
source = gracefulBlock + source;
|
|
238
339
|
}
|
|
239
340
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
/plugins\s*:\s*\[\s*\]/,
|
|
243
|
-
'plugins: [...(crank ? [crank()] : [])]'
|
|
244
|
-
);
|
|
245
|
-
} else if (/plugins\s*:\s*\[/.test(source)) {
|
|
246
|
-
source = source.replace(
|
|
247
|
-
/plugins\s*:\s*\[/,
|
|
248
|
-
'plugins: [...(crank ? [crank()] : []), '
|
|
249
|
-
);
|
|
250
|
-
} else if (/defineConfig\s*\(\s*\{/.test(source)) {
|
|
251
|
-
source = source.replace(
|
|
252
|
-
/defineConfig\s*\(\s*\{/,
|
|
253
|
-
'defineConfig({\n plugins: [...(crank ? [crank()] : [])],'
|
|
254
|
-
);
|
|
255
|
-
} else if (/export\s+default\s+\{/.test(source)) {
|
|
256
|
-
source = source.replace(
|
|
257
|
-
/export\s+default\s+\{/,
|
|
258
|
-
'export default {\n plugins: [...(crank ? [crank()] : [])],'
|
|
259
|
-
);
|
|
260
|
-
} else {
|
|
341
|
+
const plugged = ensureCrankInPlugins(source);
|
|
342
|
+
if (!plugged.ok) {
|
|
261
343
|
return {
|
|
262
344
|
patched: false,
|
|
263
345
|
reason: 'unrecognized-vite-shape',
|
|
@@ -265,33 +347,20 @@ try {
|
|
|
265
347
|
plugin: stubPath
|
|
266
348
|
};
|
|
267
349
|
}
|
|
350
|
+
source = plugged.source;
|
|
268
351
|
} else {
|
|
269
|
-
const gracefulBlock = `// Crank Vite plugin — graceful load
|
|
352
|
+
const gracefulBlock = `// Crank Vite plugin — graceful load; skip if unavailable
|
|
270
353
|
let crank = null;
|
|
271
354
|
try {
|
|
272
355
|
crank = require('${relStub}').crank;
|
|
273
356
|
} catch {}
|
|
274
357
|
|
|
275
358
|
`;
|
|
276
|
-
if (!/vite-plugin\.cjs/.test(source)) {
|
|
359
|
+
if (!/vite-plugin\.cjs/.test(source) && !/_crankPluginPath/.test(source)) {
|
|
277
360
|
source = gracefulBlock + source;
|
|
278
361
|
}
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
/plugins\s*:\s*\[\s*\]/,
|
|
282
|
-
'plugins: [...(crank ? [crank()] : [])]'
|
|
283
|
-
);
|
|
284
|
-
} else if (/plugins\s*:\s*\[/.test(source)) {
|
|
285
|
-
source = source.replace(
|
|
286
|
-
/plugins\s*:\s*\[/,
|
|
287
|
-
'plugins: [...(crank ? [crank()] : []), '
|
|
288
|
-
);
|
|
289
|
-
} else if (/module\.exports\s*=\s*\{/.test(source)) {
|
|
290
|
-
source = source.replace(
|
|
291
|
-
/module\.exports\s*=\s*\{/,
|
|
292
|
-
'module.exports = {\n plugins: [...(crank ? [crank()] : [])],'
|
|
293
|
-
);
|
|
294
|
-
} else {
|
|
362
|
+
const plugged = ensureCrankInPlugins(source);
|
|
363
|
+
if (!plugged.ok) {
|
|
295
364
|
return {
|
|
296
365
|
patched: false,
|
|
297
366
|
reason: 'unrecognized-vite-shape',
|
|
@@ -299,12 +368,27 @@ try {
|
|
|
299
368
|
plugin: stubPath
|
|
300
369
|
};
|
|
301
370
|
}
|
|
371
|
+
source = plugged.source;
|
|
302
372
|
}
|
|
303
373
|
|
|
374
|
+
// Final safety: never leave duplicate node imports.
|
|
375
|
+
source = dedupeCrankNodeImports(source).source;
|
|
376
|
+
|
|
304
377
|
fs.writeFileSync(configPath, source);
|
|
305
378
|
return { patched: true, file: configPath, plugin: stubPath };
|
|
306
379
|
}
|
|
307
380
|
|
|
381
|
+
/** Repair-only: dedupe node: imports left by older Crank Vite patches (React path). */
|
|
382
|
+
function repairViteConfigDuplicates(projectRoot) {
|
|
383
|
+
const configPath = findViteConfig(projectRoot);
|
|
384
|
+
if (!configPath) return { repaired: false, reason: 'no-vite-config' };
|
|
385
|
+
const source = fs.readFileSync(configPath, 'utf8');
|
|
386
|
+
const repaired = dedupeCrankNodeImports(source);
|
|
387
|
+
if (!repaired.changed) return { repaired: false, reason: 'clean', file: configPath };
|
|
388
|
+
fs.writeFileSync(configPath, repaired.source);
|
|
389
|
+
return { repaired: true, reason: 'repaired-duplicate-imports', file: configPath };
|
|
390
|
+
}
|
|
391
|
+
|
|
308
392
|
function writeProjectEnvExample(projectRoot) {
|
|
309
393
|
const dest = path.join(projectRoot, '.env.crank.example');
|
|
310
394
|
const src = path.join(PACKAGE_ROOT, '.env.example');
|
|
@@ -536,10 +620,19 @@ function initProject(projectRoot, options = {}) {
|
|
|
536
620
|
installCrankSkill(projectRoot, results);
|
|
537
621
|
}
|
|
538
622
|
|
|
539
|
-
// React apps:
|
|
623
|
+
// React apps: <Crank /> only — do not inject the Vite plugin (avoids duplicating
|
|
624
|
+
// node: imports when vite.config already has Vizpatch-style loaders).
|
|
625
|
+
// Still repair duplicate imports left by older Crank versions.
|
|
540
626
|
if (env.hasReact && options.react !== false) {
|
|
541
627
|
writeReactMountHint(projectRoot, env, results);
|
|
542
628
|
results.reactMount = true;
|
|
629
|
+
if (env.hasVite) {
|
|
630
|
+
const repaired = repairViteConfigDuplicates(projectRoot);
|
|
631
|
+
if (repaired.repaired) {
|
|
632
|
+
results.vite = repaired;
|
|
633
|
+
results.wrote.push(path.relative(projectRoot, repaired.file));
|
|
634
|
+
}
|
|
635
|
+
}
|
|
543
636
|
} else if (env.hasVite && options.vite !== false) {
|
|
544
637
|
const viteResult = patchViteConfig(projectRoot);
|
|
545
638
|
results.vite = viteResult;
|
|
@@ -901,6 +994,7 @@ module.exports = {
|
|
|
901
994
|
writeReplitPlatformGuide,
|
|
902
995
|
writeVitePluginStub,
|
|
903
996
|
patchViteConfig,
|
|
997
|
+
repairViteConfigDuplicates,
|
|
904
998
|
writeInjectionGuide,
|
|
905
999
|
writeReactMountHint,
|
|
906
1000
|
patchReplitConfig,
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ahmednawaz/crank",
|
|
3
|
-
"version": "0.2.
|
|
4
|
-
"description": "Internal Motive UX copy iteration: select UI text
|
|
3
|
+
"version": "0.2.6",
|
|
4
|
+
"description": "Internal Motive UX copy iteration: select UI text \u2192 Glean variants \u2192 MCP apply. Cursor, Replit, Claude.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
7
7
|
"url": "https://github.com/ahmednawaz619/crank.git"
|
|
@@ -66,7 +66,8 @@
|
|
|
66
66
|
"test:resolve": "node scripts/test-resolve-project.js",
|
|
67
67
|
"prepare:publish": "node scripts/prepare-publish.js",
|
|
68
68
|
"verify:deps": "node scripts/verify-deps.js",
|
|
69
|
-
"publish:team": "node scripts/prepare-publish.js && node scripts/verify-deps.js && npm publish --access public"
|
|
69
|
+
"publish:team": "node scripts/prepare-publish.js && node scripts/verify-deps.js && npm publish --access public",
|
|
70
|
+
"test:patch-vite": "node scripts/test-patch-vite.js"
|
|
70
71
|
},
|
|
71
72
|
"engines": {
|
|
72
73
|
"node": ">=18"
|