extension-create 4.0.25 → 4.0.27
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/dist/lib/messages.d.ts +2 -0
- package/dist/module.cjs +135 -52
- package/dist/steps/create-directory.d.ts +4 -1
- package/dist/steps/import-external-template.d.ts +7 -1
- package/dist/test-template-javascript/.extension-create.json +1 -1
- package/dist/test-template-javascript/README.md +1 -1
- package/dist/test-template-javascript/STORE.md +1 -1
- package/dist/test-template-javascript/package.json +1 -1
- package/package.json +1 -1
- package/templates/javascript/README.md +12 -10
package/dist/lib/messages.d.ts
CHANGED
|
@@ -13,6 +13,7 @@ export declare function installingFromTemplateError(template: string, error: unk
|
|
|
13
13
|
export declare function templateFetchTimedOut(templateName: string, ms: number): string;
|
|
14
14
|
export declare function templateNotFoundInCatalog(templateName: string, error?: unknown): string;
|
|
15
15
|
export declare function templateDownloadFailed(templateName: string, error: unknown): string;
|
|
16
|
+
export declare function removedStaleTemplateLockfiles(fileNames: string[]): string;
|
|
16
17
|
export declare function initializingGitForRepository(projectName: string): string;
|
|
17
18
|
export declare function initializingGitSkipped(projectName: string, reason: string): string;
|
|
18
19
|
export declare function firstCommitSkipped(projectName: string, reason: string): string;
|
|
@@ -22,6 +23,7 @@ export declare function installingProjectIntegrations(integrations: string[]): s
|
|
|
22
23
|
export declare function installingDependenciesFailed(pmCommand: string, pmArgs: string[], code: number | null): string;
|
|
23
24
|
export declare function installingDependenciesProcessError(projectName: string, error: unknown): string;
|
|
24
25
|
export declare function cantInstallDependencies(projectName: string, error: unknown): string;
|
|
26
|
+
export declare function malformedPackageJson(packageJsonPath: string, error: unknown): string;
|
|
25
27
|
export declare function writingPackageJsonMetadata(): string;
|
|
26
28
|
export declare function writingPackageJsonMetadataError(error: unknown): string;
|
|
27
29
|
export declare function writingDenoJsonc(): string;
|
package/dist/module.cjs
CHANGED
|
@@ -195,7 +195,7 @@ function writingTypeDefinitionsError(error) {
|
|
|
195
195
|
return `${prefix('error')} Couldn't write the extension type definitions.\n${fmt.label('REASON')} ${fmt.val(fmt.truncate(String(error)))}\n${external_pintor_default().red('Check the file permissions, then try again.')}`;
|
|
196
196
|
}
|
|
197
197
|
function installingFromTemplate(projectName, templateName) {
|
|
198
|
-
if (
|
|
198
|
+
if ("javascript" === templateName) return `${prefix('info')} Copying the template files…`;
|
|
199
199
|
return `${prefix('info')} Copying the template ${external_pintor_default().blue(templateName)}…`;
|
|
200
200
|
}
|
|
201
201
|
function usingTemplate(templateName, source) {
|
|
@@ -214,6 +214,9 @@ function templateNotFoundInCatalog(templateName, error) {
|
|
|
214
214
|
function templateDownloadFailed(templateName, error) {
|
|
215
215
|
return `${prefix('error')} Couldn't download the template ${external_pintor_default().blue(templateName)} from ${fmt.val('github.com/extension-js/examples')}.\n${fmt.label('REASON')} ${fmt.val(fmt.truncate(String(error?.message || error)))}\n${external_pintor_default().red('- Check your network connection and any GitHub rate limit, then try again.')}\n${external_pintor_default().red('- Set')} ${external_pintor_default().blue('EXTENSION_CREATE_TIMEOUT_MS')} ${external_pintor_default().red('to allow more time.')}`;
|
|
216
216
|
}
|
|
217
|
+
function removedStaleTemplateLockfiles(fileNames) {
|
|
218
|
+
return `${prefix('info')} Removed the template ${fileNames.map((name)=>fmt.val(name)).join(', ')}. The first install writes a fresh one.`;
|
|
219
|
+
}
|
|
217
220
|
function initializingGitForRepository(projectName) {
|
|
218
221
|
return `${prefix('debug')} create git init name=${projectName}`;
|
|
219
222
|
}
|
|
@@ -248,6 +251,9 @@ function installingDependenciesProcessError(projectName, error) {
|
|
|
248
251
|
function cantInstallDependencies(projectName, error) {
|
|
249
252
|
return `${prefix('error')} Couldn't install the dependencies for ${external_pintor_default().blue(projectName)}.\n${fmt.label('REASON')} ${fmt.val(fmt.truncate(String(error?.message || error)))}\n${external_pintor_default().red('Check your package manager settings, then try again.')}`;
|
|
250
253
|
}
|
|
254
|
+
function malformedPackageJson(packageJsonPath, error) {
|
|
255
|
+
return `${prefix('warn')} Couldn't parse ${external_pintor_default().blue('package.json')}, so no integrations were detected.\n${fmt.label('PATH')} ${fmt.val(packageJsonPath)}\n${fmt.label('REASON')} ${fmt.val(fmt.truncate(String(error?.message || error)))}`;
|
|
256
|
+
}
|
|
251
257
|
function writingPackageJsonMetadata() {
|
|
252
258
|
return `${prefix('debug')} create write file=package.json`;
|
|
253
259
|
}
|
|
@@ -377,12 +383,14 @@ async function isDirectoryWriteable(directory, logger) {
|
|
|
377
383
|
function isTypeScriptTemplate(templateName) {
|
|
378
384
|
return templateName.includes("typescript") || templateName.includes('react') || templateName.includes('preact') || templateName.includes('svelte') || templateName.includes('solid');
|
|
379
385
|
}
|
|
386
|
+
const external_node_fs_namespaceObject = require("node:fs");
|
|
380
387
|
const allowlist = [
|
|
381
388
|
'LICENSE',
|
|
382
389
|
'node_modules'
|
|
383
390
|
];
|
|
384
391
|
async function createDirectory(projectPath, projectName, logger) {
|
|
385
392
|
logger.log(startingNewExtension(projectName));
|
|
393
|
+
const directoryPreExisted = (0, external_node_fs_namespaceObject.existsSync)(projectPath);
|
|
386
394
|
try {
|
|
387
395
|
const isCurrentDirWriteable = await isDirectoryWriteable(projectPath, logger);
|
|
388
396
|
if (!isCurrentDirWriteable) {
|
|
@@ -401,6 +409,9 @@ async function createDirectory(projectPath, projectName, logger) {
|
|
|
401
409
|
} catch (error) {
|
|
402
410
|
throw new Error(createDirectoryError(projectName, error));
|
|
403
411
|
}
|
|
412
|
+
return {
|
|
413
|
+
directoryCreated: !directoryPreExisted
|
|
414
|
+
};
|
|
404
415
|
}
|
|
405
416
|
async function generateExtensionTypes(projectPath, projectName, logger) {
|
|
406
417
|
const extensionEnvFile = external_node_path_namespaceObject.join(projectPath, 'extension-env.d.ts');
|
|
@@ -427,7 +438,6 @@ async function generateExtensionTypes(projectPath, projectName, logger) {
|
|
|
427
438
|
throw error;
|
|
428
439
|
}
|
|
429
440
|
}
|
|
430
|
-
const external_node_fs_namespaceObject = require("node:fs");
|
|
431
441
|
const external_node_os_namespaceObject = require("node:os");
|
|
432
442
|
const external_adm_zip_namespaceObject = require("adm-zip");
|
|
433
443
|
var external_adm_zip_default = /*#__PURE__*/ __webpack_require__.n(external_adm_zip_namespaceObject);
|
|
@@ -589,6 +599,28 @@ async function removeTemplateScaffoldingFiles(projectPath) {
|
|
|
589
599
|
force: true
|
|
590
600
|
})));
|
|
591
601
|
}
|
|
602
|
+
const TEMPLATE_LOCKFILE_NAMES = [
|
|
603
|
+
'package-lock.json',
|
|
604
|
+
'npm-shrinkwrap.json',
|
|
605
|
+
'yarn.lock',
|
|
606
|
+
'pnpm-lock.yaml',
|
|
607
|
+
'bun.lockb',
|
|
608
|
+
'bun.lock',
|
|
609
|
+
'deno.lock'
|
|
610
|
+
];
|
|
611
|
+
async function removeStaleTemplateLockfiles(projectPath) {
|
|
612
|
+
const removed = [];
|
|
613
|
+
for (const name of TEMPLATE_LOCKFILE_NAMES){
|
|
614
|
+
const target = external_node_path_namespaceObject.join(projectPath, name);
|
|
615
|
+
if ((0, external_node_fs_namespaceObject.existsSync)(target)) {
|
|
616
|
+
await promises_namespaceObject.rm(target, {
|
|
617
|
+
force: true
|
|
618
|
+
});
|
|
619
|
+
removed.push(name);
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
return removed;
|
|
623
|
+
}
|
|
592
624
|
function getArchiveBaseName(url) {
|
|
593
625
|
const withoutQuery = url.split('?')[0];
|
|
594
626
|
const fileName = external_node_path_namespaceObject.basename(withoutQuery);
|
|
@@ -611,12 +643,35 @@ async function getZipSourcePath(tempPath, templateUrl) {
|
|
|
611
643
|
if (onlyDir.name === archiveBase) return external_node_path_namespaceObject.join(tempPath, onlyDir.name);
|
|
612
644
|
return tempPath;
|
|
613
645
|
}
|
|
614
|
-
async function
|
|
646
|
+
async function cleanupFailedImport(projectPath, ownsProjectDir, preExistingEntries) {
|
|
647
|
+
if (ownsProjectDir) return void await promises_namespaceObject.rm(projectPath, {
|
|
648
|
+
recursive: true,
|
|
649
|
+
force: true
|
|
650
|
+
}).catch(()=>{});
|
|
651
|
+
const keep = new Set(preExistingEntries);
|
|
652
|
+
let entries = [];
|
|
653
|
+
try {
|
|
654
|
+
entries = await promises_namespaceObject.readdir(projectPath);
|
|
655
|
+
} catch {
|
|
656
|
+
return;
|
|
657
|
+
}
|
|
658
|
+
await Promise.all(entries.filter((entry)=>!keep.has(entry)).map((entry)=>promises_namespaceObject.rm(external_node_path_namespaceObject.join(projectPath, entry), {
|
|
659
|
+
recursive: true,
|
|
660
|
+
force: true
|
|
661
|
+
}).catch(()=>{})));
|
|
662
|
+
}
|
|
663
|
+
async function importExternalTemplate(projectPath, projectName, template, logger, options) {
|
|
615
664
|
const templateName = external_node_path_namespaceObject.basename(template);
|
|
616
|
-
const resolvedTemplate =
|
|
617
|
-
const resolvedTemplateName =
|
|
665
|
+
const resolvedTemplate = template;
|
|
666
|
+
const resolvedTemplateName = templateName;
|
|
618
667
|
const isHttp = /^https?:\/\//i.test(template);
|
|
619
668
|
const isGithub = /^https?:\/\/github\.com\//i.test(template);
|
|
669
|
+
const dirExistedBeforeImport = (0, external_node_fs_namespaceObject.existsSync)(projectPath);
|
|
670
|
+
const ownsProjectDir = options?.ownsProjectDir ?? !dirExistedBeforeImport;
|
|
671
|
+
let preExistingEntries = [];
|
|
672
|
+
if (dirExistedBeforeImport) try {
|
|
673
|
+
preExistingEntries = await promises_namespaceObject.readdir(projectPath);
|
|
674
|
+
} catch {}
|
|
620
675
|
try {
|
|
621
676
|
await promises_namespaceObject.mkdir(projectPath, {
|
|
622
677
|
recursive: true
|
|
@@ -626,6 +681,8 @@ async function importExternalTemplate(projectPath, projectName, template, logger
|
|
|
626
681
|
if ((0, external_node_fs_namespaceObject.existsSync)(localTemplate)) {
|
|
627
682
|
await copyDirectoryWithSymlinks(localTemplate, projectPath);
|
|
628
683
|
await removeTemplateScaffoldingFiles(projectPath);
|
|
684
|
+
const dropped = await removeStaleTemplateLockfiles(projectPath);
|
|
685
|
+
if (dropped.length) logger.log(removedStaleTemplateLockfiles(dropped));
|
|
629
686
|
return {
|
|
630
687
|
template: resolvedTemplateName,
|
|
631
688
|
source: 'bundled'
|
|
@@ -696,6 +753,8 @@ async function importExternalTemplate(projectPath, projectName, template, logger
|
|
|
696
753
|
};
|
|
697
754
|
}
|
|
698
755
|
await removeTemplateScaffoldingFiles(projectPath);
|
|
756
|
+
const droppedLockfiles = await removeStaleTemplateLockfiles(projectPath);
|
|
757
|
+
if (droppedLockfiles.length) logger.log(removedStaleTemplateLockfiles(droppedLockfiles));
|
|
699
758
|
await promises_namespaceObject.rm(tempRoot, {
|
|
700
759
|
recursive: true,
|
|
701
760
|
force: true
|
|
@@ -705,10 +764,7 @@ async function importExternalTemplate(projectPath, projectName, template, logger
|
|
|
705
764
|
if (error instanceof TemplateNotFoundError) logger.error(templateNotFoundInCatalog(templateName, error.cause));
|
|
706
765
|
else if (error instanceof TemplateDownloadError) logger.error(templateDownloadFailed(templateName, error));
|
|
707
766
|
else logger.error(installingFromTemplateError(templateName, error));
|
|
708
|
-
await
|
|
709
|
-
recursive: true,
|
|
710
|
-
force: true
|
|
711
|
-
}).catch(()=>{});
|
|
767
|
+
await cleanupFailedImport(projectPath, ownsProjectDir, preExistingEntries);
|
|
712
768
|
throw error;
|
|
713
769
|
}
|
|
714
770
|
}
|
|
@@ -1025,25 +1081,25 @@ function parseNpmSpecifier(specifier) {
|
|
|
1025
1081
|
}
|
|
1026
1082
|
function readDenoConfigDependencies(projectPath) {
|
|
1027
1083
|
const dependencies = {};
|
|
1028
|
-
|
|
1029
|
-
'deno.
|
|
1030
|
-
'deno.
|
|
1031
|
-
])
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1084
|
+
const filename = [
|
|
1085
|
+
'deno.json',
|
|
1086
|
+
'deno.jsonc'
|
|
1087
|
+
].find((candidate)=>external_node_fs_namespaceObject.existsSync(external_node_path_namespaceObject.join(projectPath, candidate)));
|
|
1088
|
+
if (!filename) return dependencies;
|
|
1089
|
+
let config;
|
|
1090
|
+
try {
|
|
1091
|
+
config = parseJsoncSafe(external_node_fs_namespaceObject.readFileSync(external_node_path_namespaceObject.join(projectPath, filename), 'utf8'));
|
|
1092
|
+
} catch {
|
|
1093
|
+
return dependencies;
|
|
1094
|
+
}
|
|
1095
|
+
const imports = config?.imports;
|
|
1096
|
+
if (!imports || 'object' != typeof imports) return dependencies;
|
|
1097
|
+
for (const [rawAlias, specifier] of Object.entries(imports)){
|
|
1098
|
+
const parsed = parseNpmSpecifier(specifier);
|
|
1099
|
+
if (!parsed) continue;
|
|
1100
|
+
dependencies[parsed.name] = dependencies[parsed.name] || parsed.version;
|
|
1101
|
+
const alias = rawAlias.endsWith('/') ? rawAlias.slice(0, -1) : rawAlias;
|
|
1102
|
+
if (alias && alias !== parsed.name) dependencies[alias] = dependencies[alias] || parsed.version;
|
|
1047
1103
|
}
|
|
1048
1104
|
return dependencies;
|
|
1049
1105
|
}
|
|
@@ -1068,12 +1124,15 @@ function resolveDevelopRoot(projectPath) {
|
|
|
1068
1124
|
return null;
|
|
1069
1125
|
}
|
|
1070
1126
|
}
|
|
1071
|
-
function readPackageJson(projectPath) {
|
|
1127
|
+
function readPackageJson(projectPath, logger) {
|
|
1072
1128
|
let pkg = {};
|
|
1129
|
+
const packageJsonPath = external_node_path_namespaceObject.join(projectPath, 'package.json');
|
|
1073
1130
|
try {
|
|
1074
|
-
const raw = external_node_fs_namespaceObject.readFileSync(
|
|
1131
|
+
const raw = external_node_fs_namespaceObject.readFileSync(packageJsonPath, 'utf8');
|
|
1075
1132
|
pkg = JSON.parse(raw);
|
|
1076
|
-
} catch
|
|
1133
|
+
} catch (error) {
|
|
1134
|
+
if (error?.code !== 'ENOENT') logger.log(malformedPackageJson(packageJsonPath, error));
|
|
1135
|
+
}
|
|
1077
1136
|
const denoDependencies = readDenoConfigDependencies(projectPath);
|
|
1078
1137
|
if (Object.keys(denoDependencies).length > 0) pkg = {
|
|
1079
1138
|
...pkg,
|
|
@@ -1100,8 +1159,8 @@ function canResolve(dependency, paths) {
|
|
|
1100
1159
|
function findConfigFile(projectPath, candidates) {
|
|
1101
1160
|
return candidates.some((file)=>external_node_fs_namespaceObject.existsSync(external_node_path_namespaceObject.join(projectPath, file)));
|
|
1102
1161
|
}
|
|
1103
|
-
function detectOptionalDependencies(projectPath) {
|
|
1104
|
-
const pkg = readPackageJson(projectPath);
|
|
1162
|
+
function detectOptionalDependencies(projectPath, logger) {
|
|
1163
|
+
const pkg = readPackageJson(projectPath, logger);
|
|
1105
1164
|
const usesReact = hasDependency(pkg, 'react') || hasDependency(pkg, 'react-dom');
|
|
1106
1165
|
const usesPreact = hasDependency(pkg, 'preact');
|
|
1107
1166
|
const usesVue = hasDependency(pkg, 'vue');
|
|
@@ -1194,7 +1253,8 @@ function buildOptionalInstallArgs(pm, dependencies, installDir) {
|
|
|
1194
1253
|
...dependencies,
|
|
1195
1254
|
'--dir',
|
|
1196
1255
|
installDir,
|
|
1197
|
-
'--save-optional'
|
|
1256
|
+
'--save-optional',
|
|
1257
|
+
'--silent'
|
|
1198
1258
|
];
|
|
1199
1259
|
if ('bun' === pm) return [
|
|
1200
1260
|
'add',
|
|
@@ -1210,8 +1270,8 @@ function buildOptionalInstallArgs(pm, dependencies, installDir) {
|
|
|
1210
1270
|
'--legacy-peer-deps'
|
|
1211
1271
|
];
|
|
1212
1272
|
}
|
|
1213
|
-
function resolveMissingOptionalDeps(developRoot, projectPath) {
|
|
1214
|
-
const plan = detectOptionalDependencies(projectPath);
|
|
1273
|
+
function resolveMissingOptionalDeps(developRoot, projectPath, logger) {
|
|
1274
|
+
const plan = detectOptionalDependencies(projectPath, logger);
|
|
1215
1275
|
const dependenciesByIntegration = {};
|
|
1216
1276
|
const integrations = [];
|
|
1217
1277
|
const missing = new Set();
|
|
@@ -1260,7 +1320,7 @@ async function installInternalDependencies(projectPath, logger) {
|
|
|
1260
1320
|
if ('test' === process.env.EXTENSION_ENV || 'true' === process.env.EXTENSION_SKIP_INTERNAL_INSTALL) return;
|
|
1261
1321
|
const developRoot = resolveDevelopRoot(projectPath);
|
|
1262
1322
|
if (!developRoot) return;
|
|
1263
|
-
const optionalPlan = resolveMissingOptionalDeps(developRoot, projectPath);
|
|
1323
|
+
const optionalPlan = resolveMissingOptionalDeps(developRoot, projectPath, logger);
|
|
1264
1324
|
if (optionalPlan.dependencies.length > 0) await installOptionalDependencies(developRoot, projectPath, optionalPlan, logger);
|
|
1265
1325
|
}
|
|
1266
1326
|
async function setupBuiltInTests(projectPath, logger) {
|
|
@@ -1476,16 +1536,31 @@ async function collectTemplateImports(projectPath, cliVersion) {
|
|
|
1476
1536
|
}
|
|
1477
1537
|
async function writeDenoJsonc(projectPath, { template = "javascript", cliVersion, primary = false }, logger) {
|
|
1478
1538
|
if (!isDenoRuntime()) return;
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
'deno.json'
|
|
1482
|
-
|
|
1539
|
+
let existingConfig;
|
|
1540
|
+
for (const candidate of [
|
|
1541
|
+
'deno.json',
|
|
1542
|
+
'deno.jsonc'
|
|
1543
|
+
])if (await pathExists(external_node_path_namespaceObject.join(projectPath, candidate))) {
|
|
1544
|
+
existingConfig = candidate;
|
|
1545
|
+
break;
|
|
1546
|
+
}
|
|
1547
|
+
if (existingConfig && !primary) return;
|
|
1483
1548
|
const extensionBinary = await resolveExtensionBinary();
|
|
1484
1549
|
const tasks = getTemplateAwareScripts(template, extensionBinary);
|
|
1485
1550
|
const imports = primary ? await collectTemplateImports(projectPath, cliVersion) : void 0;
|
|
1486
1551
|
try {
|
|
1487
1552
|
if (isDebug()) logger.log(writingDenoJsonc());
|
|
1488
|
-
|
|
1553
|
+
if (existingConfig) {
|
|
1554
|
+
const configPath = external_node_path_namespaceObject.join(projectPath, existingConfig);
|
|
1555
|
+
const config = parseJsoncSafe(await promises_namespaceObject.readFile(configPath, 'utf8'));
|
|
1556
|
+
config.imports = {
|
|
1557
|
+
...imports || {},
|
|
1558
|
+
...config.imports || {}
|
|
1559
|
+
};
|
|
1560
|
+
if (void 0 === config.nodeModulesDir) config.nodeModulesDir = 'auto';
|
|
1561
|
+
if (!config.tasks) config.tasks = tasks;
|
|
1562
|
+
await promises_namespaceObject.writeFile(configPath, `${JSON.stringify(config, null, 2)}\n`);
|
|
1563
|
+
} else await promises_namespaceObject.writeFile(external_node_path_namespaceObject.join(projectPath, 'deno.jsonc'), renderDenoJsonc(tasks, imports));
|
|
1489
1564
|
if (primary) await promises_namespaceObject.rm(external_node_path_namespaceObject.join(projectPath, 'package.json'), {
|
|
1490
1565
|
force: true
|
|
1491
1566
|
});
|
|
@@ -1522,6 +1597,9 @@ const localSessionState = [
|
|
|
1522
1597
|
const envFiles = [
|
|
1523
1598
|
'',
|
|
1524
1599
|
'# local env files',
|
|
1600
|
+
'.env',
|
|
1601
|
+
'.env*',
|
|
1602
|
+
'!.env.example',
|
|
1525
1603
|
'.env.local',
|
|
1526
1604
|
'.env.development.local',
|
|
1527
1605
|
'.env.test.local',
|
|
@@ -1646,11 +1724,12 @@ async function writeReadmeFile(projectPath, projectName, logger) {
|
|
|
1646
1724
|
const manifestJsonPath = await findManifestJsonPath(projectPath);
|
|
1647
1725
|
const manifestJson = JSON.parse(await promises_namespaceObject.readFile(manifestJsonPath, 'utf-8'));
|
|
1648
1726
|
const description = String(manifestJson.description || '').trim();
|
|
1649
|
-
const
|
|
1650
|
-
const
|
|
1651
|
-
const
|
|
1727
|
+
const hasPublicScreenshot = await write_readme_file_pathExists(external_node_path_namespaceObject.join(projectPath, 'public', 'screenshot.png'));
|
|
1728
|
+
const hasRootScreenshot = await write_readme_file_pathExists(external_node_path_namespaceObject.join(projectPath, 'screenshot.png'));
|
|
1729
|
+
const screenshotHref = hasPublicScreenshot ? './public/screenshot.png' : hasRootScreenshot ? './screenshot.png' : null;
|
|
1730
|
+
const screenshotEmbed = screenshotHref ? `\n\n` : '';
|
|
1652
1731
|
const blockquote = description ? `> ${description}\n\n` : '';
|
|
1653
|
-
const readme = `<a href="https://extension.js.org" target="_blank" rel="noopener noreferrer"><img src="https://img.shields.io/badge/Powered%20by%20%7C%20Extension.js-0971fe" alt="Powered by Extension.js" align="right" /></a>\n\n# ${projectName}\n\n` + blockquote + `${screenshotEmbed}` + `## Commands\n` + `\n` + `### dev\n` + `\n` + "Run the extension in development mode. Target a browser with `--browser`:\n" + `\n` + "```bash\n" + `${runPrefix} dev\n` + `${runPrefix} dev${argSeparator} --browser=firefox\n` + `${runPrefix} dev${argSeparator} --browser=edge\n` + "```\n" + `\n` + `### build\n` + `\n` + `Build for production. Convenience scripts target each browser:\n` + `\n` + "```bash\n" + `${runPrefix} build #
|
|
1732
|
+
const readme = `<a href="https://extension.js.org" target="_blank" rel="noopener noreferrer"><img src="https://img.shields.io/badge/Powered%20by%20%7C%20Extension.js-0971fe" alt="Powered by Extension.js" align="right" /></a>\n\n# ${projectName}\n\n` + blockquote + `${screenshotEmbed}` + `## Commands\n` + `\n` + `### dev\n` + `\n` + "Run the extension in development mode. Target a browser with `--browser`:\n" + `\n` + "```bash\n" + `${runPrefix} dev\n` + `${runPrefix} dev${argSeparator} --browser=firefox\n` + `${runPrefix} dev${argSeparator} --browser=edge\n` + "```\n" + `\n` + `### build\n` + `\n` + `Build for production. Convenience scripts target each browser:\n` + `\n` + "```bash\n" + `${runPrefix} build # Chromium (default)\n` + `${runPrefix} build:firefox\n` + `${runPrefix} build:edge\n` + "```\n" + `\n` + `### preview\n` + `\n` + `Preview the production build in the browser:\n` + `\n` + "```bash\n" + `${runPrefix} preview\n` + "```\n" + `\n` + `## Learn more\n` + `\n` + `[Extension.js docs](https://extension.js.org).\n` + `\n` + `## Ship it\n` + `\n` + "Building and running your extension is local and free. When you are ready to share a build or submit it to the stores, [extension.dev](https://docs.extension.dev/publish/overview?utm_source=create-readme) " + `does that side and sponsors Extension.js.\n`;
|
|
1654
1733
|
try {
|
|
1655
1734
|
if (isDebug()) logger.log(writingReadmeMetaData());
|
|
1656
1735
|
await promises_namespaceObject.mkdir(projectPath, {
|
|
@@ -1666,11 +1745,13 @@ const STORE_METADATA_FILE = 'STORE.md';
|
|
|
1666
1745
|
function escapeForRegExp(value) {
|
|
1667
1746
|
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
1668
1747
|
}
|
|
1748
|
+
const NAME_PLACEHOLDER = '\u0000extension-create-name\u0000';
|
|
1669
1749
|
function rewriteStoreMetadata(content, projectName, templateName, today) {
|
|
1670
|
-
let next = content
|
|
1671
|
-
if (templateName && templateName !== projectName) next = next.replace(new RegExp(escapeForRegExp(templateName), 'g'),
|
|
1750
|
+
let next = content;
|
|
1751
|
+
if (templateName && templateName !== projectName) next = next.replace(new RegExp(escapeForRegExp(templateName), 'g'), NAME_PLACEHOLDER);
|
|
1752
|
+
next = next.replace(/^(\s*-\s*Name:).*$/m, (_match, label)=>`${label} ${NAME_PLACEHOLDER}`);
|
|
1672
1753
|
next = next.replace(/^(Last updated:).*$/m, (_match, label)=>`${label} ${today}`);
|
|
1673
|
-
return next;
|
|
1754
|
+
return next.split(NAME_PLACEHOLDER).join(projectName);
|
|
1674
1755
|
}
|
|
1675
1756
|
async function writeStoreMetadata(projectPath, projectName, templateName, logger) {
|
|
1676
1757
|
const storePath = external_node_path_namespaceObject.join(projectPath, STORE_METADATA_FILE);
|
|
@@ -1728,7 +1809,7 @@ async function extensionCreate(projectNameInput, { cliVersion, template = "javas
|
|
|
1728
1809
|
const projectName = external_node_path_namespaceObject.basename(projectPath);
|
|
1729
1810
|
const updateSuffix = process.env.EXTENSION_CLI_UPDATE_SUFFIX || '';
|
|
1730
1811
|
if (updateSuffix) delete process.env.EXTENSION_CLI_UPDATE_SUFFIX;
|
|
1731
|
-
const requestedTemplate =
|
|
1812
|
+
const requestedTemplate = String(template);
|
|
1732
1813
|
logger.log(' ');
|
|
1733
1814
|
logger.log(card({
|
|
1734
1815
|
version: cliVersion || process.env.EXTENSION_CLI_VERSION,
|
|
@@ -1750,8 +1831,10 @@ async function extensionCreate(projectNameInput, { cliVersion, template = "javas
|
|
|
1750
1831
|
}));
|
|
1751
1832
|
logger.log(' ');
|
|
1752
1833
|
process.env.EXTENSION_CLI_BANNER_PRINTED = 'true';
|
|
1753
|
-
await createDirectory(projectPath, projectName, logger);
|
|
1754
|
-
const templateProvenance = await importExternalTemplate(projectPath, projectName, template, logger
|
|
1834
|
+
const createResult = await createDirectory(projectPath, projectName, logger);
|
|
1835
|
+
const templateProvenance = await importExternalTemplate(projectPath, projectName, template, logger, {
|
|
1836
|
+
ownsProjectDir: createResult?.directoryCreated ?? false
|
|
1837
|
+
});
|
|
1755
1838
|
if (templateProvenance?.template) logger.log(usingTemplate(templateProvenance.template, templateProvenance.source));
|
|
1756
1839
|
const isMonorepoTemplate = String(template).toLowerCase().includes('monorepo');
|
|
1757
1840
|
if (isDenoRuntime() && !isMonorepoTemplate) await writeDenoJsonc(projectPath, {
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
+
export interface CreateDirectoryResult {
|
|
2
|
+
directoryCreated: boolean;
|
|
3
|
+
}
|
|
1
4
|
export declare function createDirectory(projectPath: string, projectName: string, logger: {
|
|
2
5
|
log(...args: unknown[]): void;
|
|
3
6
|
error(...args: unknown[]): void;
|
|
4
|
-
}): Promise<
|
|
7
|
+
}): Promise<CreateDirectoryResult>;
|
|
@@ -15,7 +15,13 @@ export declare class TemplateDownloadError extends Error {
|
|
|
15
15
|
export declare function extractExamplesTemplateFromZip(zipBuffer: Buffer, templateName: string, projectPath: string): Promise<number>;
|
|
16
16
|
export declare const TEMPLATE_SCAFFOLDING_FILES: string[];
|
|
17
17
|
export declare function removeTemplateScaffoldingFiles(projectPath: string): Promise<void>;
|
|
18
|
+
export declare const TEMPLATE_LOCKFILE_NAMES: string[];
|
|
19
|
+
export declare function removeStaleTemplateLockfiles(projectPath: string): Promise<string[]>;
|
|
20
|
+
export interface ImportExternalTemplateOptions {
|
|
21
|
+
ownsProjectDir?: boolean;
|
|
22
|
+
}
|
|
23
|
+
export declare function cleanupFailedImport(projectPath: string, ownsProjectDir: boolean, preExistingEntries: string[]): Promise<void>;
|
|
18
24
|
export declare function importExternalTemplate(projectPath: string, projectName: string, template: string, logger: {
|
|
19
25
|
log(...args: unknown[]): void;
|
|
20
26
|
error(...args: unknown[]): void;
|
|
21
|
-
}): Promise<TemplateProvenance>;
|
|
27
|
+
}, options?: ImportExternalTemplateOptions): Promise<TemplateProvenance>;
|
|
@@ -9,7 +9,7 @@ Packaging your extension is local and free. Submitting the result to a
|
|
|
9
9
|
store is what [extension.dev](https://docs.extension.dev/publish/overview?utm_source=store-md)
|
|
10
10
|
does, and it sponsors Extension.js.
|
|
11
11
|
|
|
12
|
-
Last updated: 2026-
|
|
12
|
+
Last updated: 2026-08-02
|
|
13
13
|
|
|
14
14
|
## Listing
|
|
15
15
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
[powered-image]: https://img.shields.io/badge/Powered%20by-Extension.js-0971fe
|
|
2
2
|
[powered-url]: https://extension.js.org
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
![Powered by Extension.js][powered-image]
|
|
5
5
|
|
|
6
6
|
# JavaScript Starter Extension
|
|
7
7
|
|
|
@@ -47,25 +47,27 @@ src/
|
|
|
47
47
|
|
|
48
48
|
## Commands
|
|
49
49
|
|
|
50
|
+
Cloned this repo instead? The examples ship without npm scripts, so run Extension.js directly from the example directory. Run `npm install` first when the example declares dependencies.
|
|
51
|
+
|
|
50
52
|
### dev
|
|
51
53
|
|
|
52
54
|
Run the extension in development mode. Target a browser with `--browser`:
|
|
53
55
|
|
|
54
56
|
```bash
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
57
|
+
npx extension@latest dev . # Chromium (default)
|
|
58
|
+
npx extension@latest dev . --browser=chrome
|
|
59
|
+
npx extension@latest dev . --browser=edge
|
|
60
|
+
npx extension@latest dev . --browser=firefox
|
|
59
61
|
```
|
|
60
62
|
|
|
61
63
|
### build
|
|
62
64
|
|
|
63
|
-
Build for production
|
|
65
|
+
Build for production:
|
|
64
66
|
|
|
65
67
|
```bash
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
68
|
+
npx extension@latest build . # Chromium (default)
|
|
69
|
+
npx extension@latest build . --browser=firefox
|
|
70
|
+
npx extension@latest build . --browser=edge
|
|
69
71
|
```
|
|
70
72
|
|
|
71
73
|
### preview
|
|
@@ -73,7 +75,7 @@ npm run build:edge
|
|
|
73
75
|
Preview the production build with the bundled browser:
|
|
74
76
|
|
|
75
77
|
```bash
|
|
76
|
-
|
|
78
|
+
npx extension@latest preview .
|
|
77
79
|
```
|
|
78
80
|
|
|
79
81
|
## Tests
|