extension-create 4.0.26 → 4.0.28
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 -61
- package/dist/steps/create-directory.d.ts +4 -1
- package/dist/steps/import-external-template.d.ts +8 -1
- package/dist/test-template-javascript/.extension-create.json +1 -1
- package/dist/test-template-javascript/README.md +1 -3
- 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 +13 -11
- package/templates/javascript/package.json +1 -6
- package/templates/javascript/public/screenshot.png +0 -0
- /package/{dist/test-template-javascript/public → templates/javascript}/screenshot.png +0 -0
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);
|
|
@@ -450,6 +460,7 @@ const NETWORK_TIMEOUT_MS = (()=>{
|
|
|
450
460
|
return Number.isFinite(raw) && raw > 0 ? raw : 60000;
|
|
451
461
|
})();
|
|
452
462
|
const CODELOAD_BASE = 'https://codeload.github.com/extension-js/examples/zip';
|
|
463
|
+
const DEFAULT_TEMPLATES_REF = '52c0d871c433d9c54878767175ce8ffc9a951756';
|
|
453
464
|
function resolveCatalogUrls(ref, overrideUrl) {
|
|
454
465
|
if (overrideUrl) return [
|
|
455
466
|
overrideUrl
|
|
@@ -526,7 +537,7 @@ async function extractExamplesTemplateFromZip(zipBuffer, templateName, projectPa
|
|
|
526
537
|
return written;
|
|
527
538
|
}
|
|
528
539
|
async function importFromExamplesCatalog(templateName, projectPath) {
|
|
529
|
-
const ref = process.env.EXTENSION_CREATE_TEMPLATE_REF ||
|
|
540
|
+
const ref = process.env.EXTENSION_CREATE_TEMPLATE_REF || DEFAULT_TEMPLATES_REF;
|
|
530
541
|
const overrideUrl = process.env.EXTENSION_CREATE_TEMPLATE_URL || void 0;
|
|
531
542
|
const urls = resolveCatalogUrls(ref, overrideUrl);
|
|
532
543
|
let buffer;
|
|
@@ -589,6 +600,28 @@ async function removeTemplateScaffoldingFiles(projectPath) {
|
|
|
589
600
|
force: true
|
|
590
601
|
})));
|
|
591
602
|
}
|
|
603
|
+
const TEMPLATE_LOCKFILE_NAMES = [
|
|
604
|
+
'package-lock.json',
|
|
605
|
+
'npm-shrinkwrap.json',
|
|
606
|
+
'yarn.lock',
|
|
607
|
+
'pnpm-lock.yaml',
|
|
608
|
+
'bun.lockb',
|
|
609
|
+
'bun.lock',
|
|
610
|
+
'deno.lock'
|
|
611
|
+
];
|
|
612
|
+
async function removeStaleTemplateLockfiles(projectPath) {
|
|
613
|
+
const removed = [];
|
|
614
|
+
for (const name of TEMPLATE_LOCKFILE_NAMES){
|
|
615
|
+
const target = external_node_path_namespaceObject.join(projectPath, name);
|
|
616
|
+
if ((0, external_node_fs_namespaceObject.existsSync)(target)) {
|
|
617
|
+
await promises_namespaceObject.rm(target, {
|
|
618
|
+
force: true
|
|
619
|
+
});
|
|
620
|
+
removed.push(name);
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
return removed;
|
|
624
|
+
}
|
|
592
625
|
function getArchiveBaseName(url) {
|
|
593
626
|
const withoutQuery = url.split('?')[0];
|
|
594
627
|
const fileName = external_node_path_namespaceObject.basename(withoutQuery);
|
|
@@ -611,12 +644,35 @@ async function getZipSourcePath(tempPath, templateUrl) {
|
|
|
611
644
|
if (onlyDir.name === archiveBase) return external_node_path_namespaceObject.join(tempPath, onlyDir.name);
|
|
612
645
|
return tempPath;
|
|
613
646
|
}
|
|
614
|
-
async function
|
|
647
|
+
async function cleanupFailedImport(projectPath, ownsProjectDir, preExistingEntries) {
|
|
648
|
+
if (ownsProjectDir) return void await promises_namespaceObject.rm(projectPath, {
|
|
649
|
+
recursive: true,
|
|
650
|
+
force: true
|
|
651
|
+
}).catch(()=>{});
|
|
652
|
+
const keep = new Set(preExistingEntries);
|
|
653
|
+
let entries = [];
|
|
654
|
+
try {
|
|
655
|
+
entries = await promises_namespaceObject.readdir(projectPath);
|
|
656
|
+
} catch {
|
|
657
|
+
return;
|
|
658
|
+
}
|
|
659
|
+
await Promise.all(entries.filter((entry)=>!keep.has(entry)).map((entry)=>promises_namespaceObject.rm(external_node_path_namespaceObject.join(projectPath, entry), {
|
|
660
|
+
recursive: true,
|
|
661
|
+
force: true
|
|
662
|
+
}).catch(()=>{})));
|
|
663
|
+
}
|
|
664
|
+
async function importExternalTemplate(projectPath, projectName, template, logger, options) {
|
|
615
665
|
const templateName = external_node_path_namespaceObject.basename(template);
|
|
616
666
|
const resolvedTemplate = template;
|
|
617
667
|
const resolvedTemplateName = templateName;
|
|
618
668
|
const isHttp = /^https?:\/\//i.test(template);
|
|
619
669
|
const isGithub = /^https?:\/\/github\.com\//i.test(template);
|
|
670
|
+
const dirExistedBeforeImport = (0, external_node_fs_namespaceObject.existsSync)(projectPath);
|
|
671
|
+
const ownsProjectDir = options?.ownsProjectDir ?? !dirExistedBeforeImport;
|
|
672
|
+
let preExistingEntries = [];
|
|
673
|
+
if (dirExistedBeforeImport) try {
|
|
674
|
+
preExistingEntries = await promises_namespaceObject.readdir(projectPath);
|
|
675
|
+
} catch {}
|
|
620
676
|
try {
|
|
621
677
|
await promises_namespaceObject.mkdir(projectPath, {
|
|
622
678
|
recursive: true
|
|
@@ -626,6 +682,8 @@ async function importExternalTemplate(projectPath, projectName, template, logger
|
|
|
626
682
|
if ((0, external_node_fs_namespaceObject.existsSync)(localTemplate)) {
|
|
627
683
|
await copyDirectoryWithSymlinks(localTemplate, projectPath);
|
|
628
684
|
await removeTemplateScaffoldingFiles(projectPath);
|
|
685
|
+
const dropped = await removeStaleTemplateLockfiles(projectPath);
|
|
686
|
+
if (dropped.length) logger.log(removedStaleTemplateLockfiles(dropped));
|
|
629
687
|
return {
|
|
630
688
|
template: resolvedTemplateName,
|
|
631
689
|
source: 'bundled'
|
|
@@ -696,6 +754,8 @@ async function importExternalTemplate(projectPath, projectName, template, logger
|
|
|
696
754
|
};
|
|
697
755
|
}
|
|
698
756
|
await removeTemplateScaffoldingFiles(projectPath);
|
|
757
|
+
const droppedLockfiles = await removeStaleTemplateLockfiles(projectPath);
|
|
758
|
+
if (droppedLockfiles.length) logger.log(removedStaleTemplateLockfiles(droppedLockfiles));
|
|
699
759
|
await promises_namespaceObject.rm(tempRoot, {
|
|
700
760
|
recursive: true,
|
|
701
761
|
force: true
|
|
@@ -705,10 +765,7 @@ async function importExternalTemplate(projectPath, projectName, template, logger
|
|
|
705
765
|
if (error instanceof TemplateNotFoundError) logger.error(templateNotFoundInCatalog(templateName, error.cause));
|
|
706
766
|
else if (error instanceof TemplateDownloadError) logger.error(templateDownloadFailed(templateName, error));
|
|
707
767
|
else logger.error(installingFromTemplateError(templateName, error));
|
|
708
|
-
await
|
|
709
|
-
recursive: true,
|
|
710
|
-
force: true
|
|
711
|
-
}).catch(()=>{});
|
|
768
|
+
await cleanupFailedImport(projectPath, ownsProjectDir, preExistingEntries);
|
|
712
769
|
throw error;
|
|
713
770
|
}
|
|
714
771
|
}
|
|
@@ -1025,25 +1082,25 @@ function parseNpmSpecifier(specifier) {
|
|
|
1025
1082
|
}
|
|
1026
1083
|
function readDenoConfigDependencies(projectPath) {
|
|
1027
1084
|
const dependencies = {};
|
|
1028
|
-
|
|
1029
|
-
'deno.
|
|
1030
|
-
'deno.
|
|
1031
|
-
])
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1085
|
+
const filename = [
|
|
1086
|
+
'deno.json',
|
|
1087
|
+
'deno.jsonc'
|
|
1088
|
+
].find((candidate)=>external_node_fs_namespaceObject.existsSync(external_node_path_namespaceObject.join(projectPath, candidate)));
|
|
1089
|
+
if (!filename) return dependencies;
|
|
1090
|
+
let config;
|
|
1091
|
+
try {
|
|
1092
|
+
config = parseJsoncSafe(external_node_fs_namespaceObject.readFileSync(external_node_path_namespaceObject.join(projectPath, filename), 'utf8'));
|
|
1093
|
+
} catch {
|
|
1094
|
+
return dependencies;
|
|
1095
|
+
}
|
|
1096
|
+
const imports = config?.imports;
|
|
1097
|
+
if (!imports || 'object' != typeof imports) return dependencies;
|
|
1098
|
+
for (const [rawAlias, specifier] of Object.entries(imports)){
|
|
1099
|
+
const parsed = parseNpmSpecifier(specifier);
|
|
1100
|
+
if (!parsed) continue;
|
|
1101
|
+
dependencies[parsed.name] = dependencies[parsed.name] || parsed.version;
|
|
1102
|
+
const alias = rawAlias.endsWith('/') ? rawAlias.slice(0, -1) : rawAlias;
|
|
1103
|
+
if (alias && alias !== parsed.name) dependencies[alias] = dependencies[alias] || parsed.version;
|
|
1047
1104
|
}
|
|
1048
1105
|
return dependencies;
|
|
1049
1106
|
}
|
|
@@ -1068,12 +1125,15 @@ function resolveDevelopRoot(projectPath) {
|
|
|
1068
1125
|
return null;
|
|
1069
1126
|
}
|
|
1070
1127
|
}
|
|
1071
|
-
function readPackageJson(projectPath) {
|
|
1128
|
+
function readPackageJson(projectPath, logger) {
|
|
1072
1129
|
let pkg = {};
|
|
1130
|
+
const packageJsonPath = external_node_path_namespaceObject.join(projectPath, 'package.json');
|
|
1073
1131
|
try {
|
|
1074
|
-
const raw = external_node_fs_namespaceObject.readFileSync(
|
|
1132
|
+
const raw = external_node_fs_namespaceObject.readFileSync(packageJsonPath, 'utf8');
|
|
1075
1133
|
pkg = JSON.parse(raw);
|
|
1076
|
-
} catch
|
|
1134
|
+
} catch (error) {
|
|
1135
|
+
if (error?.code !== 'ENOENT') logger.log(malformedPackageJson(packageJsonPath, error));
|
|
1136
|
+
}
|
|
1077
1137
|
const denoDependencies = readDenoConfigDependencies(projectPath);
|
|
1078
1138
|
if (Object.keys(denoDependencies).length > 0) pkg = {
|
|
1079
1139
|
...pkg,
|
|
@@ -1100,8 +1160,8 @@ function canResolve(dependency, paths) {
|
|
|
1100
1160
|
function findConfigFile(projectPath, candidates) {
|
|
1101
1161
|
return candidates.some((file)=>external_node_fs_namespaceObject.existsSync(external_node_path_namespaceObject.join(projectPath, file)));
|
|
1102
1162
|
}
|
|
1103
|
-
function detectOptionalDependencies(projectPath) {
|
|
1104
|
-
const pkg = readPackageJson(projectPath);
|
|
1163
|
+
function detectOptionalDependencies(projectPath, logger) {
|
|
1164
|
+
const pkg = readPackageJson(projectPath, logger);
|
|
1105
1165
|
const usesReact = hasDependency(pkg, 'react') || hasDependency(pkg, 'react-dom');
|
|
1106
1166
|
const usesPreact = hasDependency(pkg, 'preact');
|
|
1107
1167
|
const usesVue = hasDependency(pkg, 'vue');
|
|
@@ -1194,7 +1254,8 @@ function buildOptionalInstallArgs(pm, dependencies, installDir) {
|
|
|
1194
1254
|
...dependencies,
|
|
1195
1255
|
'--dir',
|
|
1196
1256
|
installDir,
|
|
1197
|
-
'--save-optional'
|
|
1257
|
+
'--save-optional',
|
|
1258
|
+
'--silent'
|
|
1198
1259
|
];
|
|
1199
1260
|
if ('bun' === pm) return [
|
|
1200
1261
|
'add',
|
|
@@ -1210,8 +1271,8 @@ function buildOptionalInstallArgs(pm, dependencies, installDir) {
|
|
|
1210
1271
|
'--legacy-peer-deps'
|
|
1211
1272
|
];
|
|
1212
1273
|
}
|
|
1213
|
-
function resolveMissingOptionalDeps(developRoot, projectPath) {
|
|
1214
|
-
const plan = detectOptionalDependencies(projectPath);
|
|
1274
|
+
function resolveMissingOptionalDeps(developRoot, projectPath, logger) {
|
|
1275
|
+
const plan = detectOptionalDependencies(projectPath, logger);
|
|
1215
1276
|
const dependenciesByIntegration = {};
|
|
1216
1277
|
const integrations = [];
|
|
1217
1278
|
const missing = new Set();
|
|
@@ -1260,7 +1321,7 @@ async function installInternalDependencies(projectPath, logger) {
|
|
|
1260
1321
|
if ('test' === process.env.EXTENSION_ENV || 'true' === process.env.EXTENSION_SKIP_INTERNAL_INSTALL) return;
|
|
1261
1322
|
const developRoot = resolveDevelopRoot(projectPath);
|
|
1262
1323
|
if (!developRoot) return;
|
|
1263
|
-
const optionalPlan = resolveMissingOptionalDeps(developRoot, projectPath);
|
|
1324
|
+
const optionalPlan = resolveMissingOptionalDeps(developRoot, projectPath, logger);
|
|
1264
1325
|
if (optionalPlan.dependencies.length > 0) await installOptionalDependencies(developRoot, projectPath, optionalPlan, logger);
|
|
1265
1326
|
}
|
|
1266
1327
|
async function setupBuiltInTests(projectPath, logger) {
|
|
@@ -1392,13 +1453,6 @@ async function overridePackageJson(projectPath, { template = "javascript", cliVe
|
|
|
1392
1453
|
onlyBuiltDependencies: onlyBuilt
|
|
1393
1454
|
} : {}
|
|
1394
1455
|
};
|
|
1395
|
-
const identity = readGitIdentity(projectPath);
|
|
1396
|
-
const author = identity.name ? {
|
|
1397
|
-
name: identity.name,
|
|
1398
|
-
...identity.email ? {
|
|
1399
|
-
email: identity.email
|
|
1400
|
-
} : {}
|
|
1401
|
-
} : void 0;
|
|
1402
1456
|
const templateFields = {
|
|
1403
1457
|
...packageJson
|
|
1404
1458
|
};
|
|
@@ -1421,9 +1475,6 @@ async function overridePackageJson(projectPath, { template = "javascript", cliVe
|
|
|
1421
1475
|
} : {},
|
|
1422
1476
|
...trustedDeps.length ? {
|
|
1423
1477
|
trustedDependencies: trustedDeps
|
|
1424
|
-
} : {},
|
|
1425
|
-
...author ? {
|
|
1426
|
-
author
|
|
1427
1478
|
} : {}
|
|
1428
1479
|
};
|
|
1429
1480
|
try {
|
|
@@ -1476,16 +1527,31 @@ async function collectTemplateImports(projectPath, cliVersion) {
|
|
|
1476
1527
|
}
|
|
1477
1528
|
async function writeDenoJsonc(projectPath, { template = "javascript", cliVersion, primary = false }, logger) {
|
|
1478
1529
|
if (!isDenoRuntime()) return;
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
'deno.json'
|
|
1482
|
-
|
|
1530
|
+
let existingConfig;
|
|
1531
|
+
for (const candidate of [
|
|
1532
|
+
'deno.json',
|
|
1533
|
+
'deno.jsonc'
|
|
1534
|
+
])if (await pathExists(external_node_path_namespaceObject.join(projectPath, candidate))) {
|
|
1535
|
+
existingConfig = candidate;
|
|
1536
|
+
break;
|
|
1537
|
+
}
|
|
1538
|
+
if (existingConfig && !primary) return;
|
|
1483
1539
|
const extensionBinary = await resolveExtensionBinary();
|
|
1484
1540
|
const tasks = getTemplateAwareScripts(template, extensionBinary);
|
|
1485
1541
|
const imports = primary ? await collectTemplateImports(projectPath, cliVersion) : void 0;
|
|
1486
1542
|
try {
|
|
1487
1543
|
if (isDebug()) logger.log(writingDenoJsonc());
|
|
1488
|
-
|
|
1544
|
+
if (existingConfig) {
|
|
1545
|
+
const configPath = external_node_path_namespaceObject.join(projectPath, existingConfig);
|
|
1546
|
+
const config = parseJsoncSafe(await promises_namespaceObject.readFile(configPath, 'utf8'));
|
|
1547
|
+
config.imports = {
|
|
1548
|
+
...imports || {},
|
|
1549
|
+
...config.imports || {}
|
|
1550
|
+
};
|
|
1551
|
+
if (void 0 === config.nodeModulesDir) config.nodeModulesDir = 'auto';
|
|
1552
|
+
if (!config.tasks) config.tasks = tasks;
|
|
1553
|
+
await promises_namespaceObject.writeFile(configPath, `${JSON.stringify(config, null, 2)}\n`);
|
|
1554
|
+
} else await promises_namespaceObject.writeFile(external_node_path_namespaceObject.join(projectPath, 'deno.jsonc'), renderDenoJsonc(tasks, imports));
|
|
1489
1555
|
if (primary) await promises_namespaceObject.rm(external_node_path_namespaceObject.join(projectPath, 'package.json'), {
|
|
1490
1556
|
force: true
|
|
1491
1557
|
});
|
|
@@ -1522,6 +1588,9 @@ const localSessionState = [
|
|
|
1522
1588
|
const envFiles = [
|
|
1523
1589
|
'',
|
|
1524
1590
|
'# local env files',
|
|
1591
|
+
'.env',
|
|
1592
|
+
'.env*',
|
|
1593
|
+
'!.env.example',
|
|
1525
1594
|
'.env.local',
|
|
1526
1595
|
'.env.development.local',
|
|
1527
1596
|
'.env.test.local',
|
|
@@ -1646,11 +1715,12 @@ async function writeReadmeFile(projectPath, projectName, logger) {
|
|
|
1646
1715
|
const manifestJsonPath = await findManifestJsonPath(projectPath);
|
|
1647
1716
|
const manifestJson = JSON.parse(await promises_namespaceObject.readFile(manifestJsonPath, 'utf-8'));
|
|
1648
1717
|
const description = String(manifestJson.description || '').trim();
|
|
1649
|
-
const
|
|
1650
|
-
const
|
|
1651
|
-
const
|
|
1718
|
+
const hasPublicScreenshot = await write_readme_file_pathExists(external_node_path_namespaceObject.join(projectPath, 'public', 'screenshot.png'));
|
|
1719
|
+
const hasRootScreenshot = await write_readme_file_pathExists(external_node_path_namespaceObject.join(projectPath, 'screenshot.png'));
|
|
1720
|
+
const screenshotHref = hasPublicScreenshot ? './public/screenshot.png' : hasRootScreenshot ? './screenshot.png' : null;
|
|
1721
|
+
const screenshotEmbed = screenshotHref ? `\n\n` : '';
|
|
1652
1722
|
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 #
|
|
1723
|
+
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
1724
|
try {
|
|
1655
1725
|
if (isDebug()) logger.log(writingReadmeMetaData());
|
|
1656
1726
|
await promises_namespaceObject.mkdir(projectPath, {
|
|
@@ -1666,11 +1736,13 @@ const STORE_METADATA_FILE = 'STORE.md';
|
|
|
1666
1736
|
function escapeForRegExp(value) {
|
|
1667
1737
|
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
1668
1738
|
}
|
|
1739
|
+
const NAME_PLACEHOLDER = '\u0000extension-create-name\u0000';
|
|
1669
1740
|
function rewriteStoreMetadata(content, projectName, templateName, today) {
|
|
1670
|
-
let next = content
|
|
1671
|
-
if (templateName && templateName !== projectName) next = next.replace(new RegExp(escapeForRegExp(templateName), 'g'),
|
|
1741
|
+
let next = content;
|
|
1742
|
+
if (templateName && templateName !== projectName) next = next.replace(new RegExp(escapeForRegExp(templateName), 'g'), NAME_PLACEHOLDER);
|
|
1743
|
+
next = next.replace(/^(\s*-\s*Name:).*$/m, (_match, label)=>`${label} ${NAME_PLACEHOLDER}`);
|
|
1672
1744
|
next = next.replace(/^(Last updated:).*$/m, (_match, label)=>`${label} ${today}`);
|
|
1673
|
-
return next;
|
|
1745
|
+
return next.split(NAME_PLACEHOLDER).join(projectName);
|
|
1674
1746
|
}
|
|
1675
1747
|
async function writeStoreMetadata(projectPath, projectName, templateName, logger) {
|
|
1676
1748
|
const storePath = external_node_path_namespaceObject.join(projectPath, STORE_METADATA_FILE);
|
|
@@ -1728,7 +1800,7 @@ async function extensionCreate(projectNameInput, { cliVersion, template = "javas
|
|
|
1728
1800
|
const projectName = external_node_path_namespaceObject.basename(projectPath);
|
|
1729
1801
|
const updateSuffix = process.env.EXTENSION_CLI_UPDATE_SUFFIX || '';
|
|
1730
1802
|
if (updateSuffix) delete process.env.EXTENSION_CLI_UPDATE_SUFFIX;
|
|
1731
|
-
const requestedTemplate =
|
|
1803
|
+
const requestedTemplate = String(template);
|
|
1732
1804
|
logger.log(' ');
|
|
1733
1805
|
logger.log(card({
|
|
1734
1806
|
version: cliVersion || process.env.EXTENSION_CLI_VERSION,
|
|
@@ -1750,8 +1822,10 @@ async function extensionCreate(projectNameInput, { cliVersion, template = "javas
|
|
|
1750
1822
|
}));
|
|
1751
1823
|
logger.log(' ');
|
|
1752
1824
|
process.env.EXTENSION_CLI_BANNER_PRINTED = 'true';
|
|
1753
|
-
await createDirectory(projectPath, projectName, logger);
|
|
1754
|
-
const templateProvenance = await importExternalTemplate(projectPath, projectName, template, logger
|
|
1825
|
+
const createResult = await createDirectory(projectPath, projectName, logger);
|
|
1826
|
+
const templateProvenance = await importExternalTemplate(projectPath, projectName, template, logger, {
|
|
1827
|
+
ownsProjectDir: createResult?.directoryCreated ?? false
|
|
1828
|
+
});
|
|
1755
1829
|
if (templateProvenance?.template) logger.log(usingTemplate(templateProvenance.template, templateProvenance.source));
|
|
1756
1830
|
const isMonorepoTemplate = String(template).toLowerCase().includes('monorepo');
|
|
1757
1831
|
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>;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export declare const DEFAULT_TEMPLATES_REF = "52c0d871c433d9c54878767175ce8ffc9a951756";
|
|
1
2
|
export declare function resolveCatalogUrls(ref: string, overrideUrl?: string): string[];
|
|
2
3
|
export interface TemplateProvenance {
|
|
3
4
|
template: string;
|
|
@@ -15,7 +16,13 @@ export declare class TemplateDownloadError extends Error {
|
|
|
15
16
|
export declare function extractExamplesTemplateFromZip(zipBuffer: Buffer, templateName: string, projectPath: string): Promise<number>;
|
|
16
17
|
export declare const TEMPLATE_SCAFFOLDING_FILES: string[];
|
|
17
18
|
export declare function removeTemplateScaffoldingFiles(projectPath: string): Promise<void>;
|
|
19
|
+
export declare const TEMPLATE_LOCKFILE_NAMES: string[];
|
|
20
|
+
export declare function removeStaleTemplateLockfiles(projectPath: string): Promise<string[]>;
|
|
21
|
+
export interface ImportExternalTemplateOptions {
|
|
22
|
+
ownsProjectDir?: boolean;
|
|
23
|
+
}
|
|
24
|
+
export declare function cleanupFailedImport(projectPath: string, ownsProjectDir: boolean, preExistingEntries: string[]): Promise<void>;
|
|
18
25
|
export declare function importExternalTemplate(projectPath: string, projectName: string, template: string, logger: {
|
|
19
26
|
log(...args: unknown[]): void;
|
|
20
27
|
error(...args: unknown[]): void;
|
|
21
|
-
}): Promise<TemplateProvenance>;
|
|
28
|
+
}, options?: ImportExternalTemplateOptions): Promise<TemplateProvenance>;
|
|
@@ -4,8 +4,6 @@
|
|
|
4
4
|
|
|
5
5
|
> Adds a sidebar panel to the browser with a simple page.
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-

|
|
9
7
|
## Commands
|
|
10
8
|
|
|
11
9
|
### dev
|
|
@@ -23,7 +21,7 @@ pnpm run dev -- --browser=edge
|
|
|
23
21
|
Build for production. Convenience scripts target each browser:
|
|
24
22
|
|
|
25
23
|
```bash
|
|
26
|
-
pnpm run build #
|
|
24
|
+
pnpm run build # Chromium (default)
|
|
27
25
|
pnpm run build:firefox
|
|
28
26
|
pnpm run build:edge
|
|
29
27
|
```
|
|
@@ -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-03
|
|
13
13
|
|
|
14
14
|
## Listing
|
|
15
15
|
|
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
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
|
|
|
8
8
|
> Adds a sidebar panel to the browser with a simple page.
|
|
9
9
|
|
|
10
|
-

|
|
11
11
|
|
|
12
12
|
**What you'll see**: A small UI injected into any web page, isolated in a Shadow DOM so site styles don't bleed through.
|
|
13
13
|
|
|
@@ -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
|
|
@@ -4,10 +4,5 @@
|
|
|
4
4
|
"description": "Adds a sidebar panel to the browser with a simple page.",
|
|
5
5
|
"version": "1.0.0",
|
|
6
6
|
"license": "MIT",
|
|
7
|
-
"type": "module"
|
|
8
|
-
"author": {
|
|
9
|
-
"name": "Cezar Augusto",
|
|
10
|
-
"email": "boss@cezaraugusto.net",
|
|
11
|
-
"url": "https://cezaraugusto.com"
|
|
12
|
-
}
|
|
7
|
+
"type": "module"
|
|
13
8
|
}
|
|
Binary file
|
|
File without changes
|