create-gasket-app 7.4.11 → 7.4.13

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/EXAMPLES.md ADDED
@@ -0,0 +1,48 @@
1
+ # Create Gasket App Examples
2
+
3
+ This document provides working examples for interfaces and utilities from the `create-gasket-app` package.
4
+
5
+ ## Primary Usage (CLI)
6
+
7
+ The main interface for `create-gasket-app` is the command line:
8
+
9
+ ```bash
10
+ # Basic app creation
11
+ npx create-gasket-app@latest my-app
12
+
13
+ # With presets
14
+ npx create-gasket-app@latest my-app --presets @gasket/preset-nextjs
15
+
16
+ # With package manager selection
17
+ npx create-gasket-app@latest my-app --package-manager pnpm
18
+
19
+ # With configuration
20
+ npx create-gasket-app@latest my-app --config '{"typescript": true}'
21
+
22
+ # With config file
23
+ npx create-gasket-app@latest my-app --config-file ./create-config.json
24
+
25
+ # Skip prompts (for CI)
26
+ npx create-gasket-app@latest my-app --presets @gasket/preset-nextjs --no-prompts
27
+ ```
28
+
29
+ ## Templates
30
+
31
+ Templates provide complete Gasket applications ready to use:
32
+
33
+ ```bash
34
+ # Use an npm template package
35
+ npx create-gasket-app@latest my-app --template @gasket/template-nextjs-pages-js
36
+
37
+ # Use a versioned template
38
+ npx create-gasket-app@latest my-app --template @gasket/template-api@^2.0.0
39
+
40
+ # Use a tagged template
41
+ npx create-gasket-app@latest my-app --template @gasket/template-nextjs@beta
42
+
43
+ # Use a local template during development
44
+ npx create-gasket-app@latest my-app --template-path ./my-gasket-template
45
+
46
+ # Use local template with absolute path
47
+ npx create-gasket-app@latest my-app --template-path /path/to/my-template
48
+ ```
package/README.md CHANGED
@@ -43,6 +43,10 @@ Options:
43
43
  -p, --presets [presets] Initial Gasket preset(s) to use.
44
44
  Can be set as short name with version (e.g. --presets nextjs@^1.0.0)
45
45
  Or other (multiple) custom presets (e.g. --presets my-gasket-preset@1.0.0.beta-1,nextjs@^1.0.0)
46
+ --template [template] Selects which template you would like to use during
47
+ installation. (e.g. --template @gasket/template-nextjs-pages-js)
48
+ --template-path [template-path] (INTERNAL) Path to a local template package. Can be absolute
49
+ or relative to the current working directory.
46
50
  --package-manager [package-manager] Selects which package manager you would like to use during
47
51
  installation. (e.g. --package-manager yarn)
48
52
  -r, --require [require] Require module(s) before Gasket is initialized
@@ -51,6 +55,48 @@ Options:
51
55
  -h, --help display help for command
52
56
  ```
53
57
 
58
+ #### Templates
59
+
60
+ Templates provide a complete Gasket application ready to use, unlike presets which build apps through a plugin-based creation process.
61
+
62
+ ##### Using Templates
63
+
64
+ ```bash
65
+ # Use an official template from npm
66
+ npx create-gasket-app@latest my-app --template @gasket/template-nextjs-pages-js
67
+
68
+ # Use a versioned template
69
+ npx create-gasket-app@latest my-app --template @gasket/template-api@^2.0.0
70
+
71
+ # Use a local template (development)
72
+ npx create-gasket-app@latest my-app --template-path ./path/to/my-template
73
+
74
+ # Use a template with tag
75
+ npx create-gasket-app@latest my-app --template @gasket/template-nextjs-pages-js@beta
76
+ ```
77
+
78
+ ##### Template Structure
79
+
80
+ Templates are npm packages that follow this structure:
81
+
82
+ ```
83
+ @gasket/template-example/
84
+ ├── package.json
85
+ ├── template/
86
+ │ ├── package.json # App's package.json
87
+ │ ├── gasket.js # App's gasket config
88
+ │ ├── src/ # App source files
89
+ │ └── ... # Other app files
90
+ └── README.md
91
+ ```
92
+
93
+ When using templates:
94
+ - The entire `template/` directory is copied to your new app
95
+ - Template dependencies are installed with `npm ci`
96
+ - No preset processing or plugin creation flows are run
97
+
98
+ **Note:** Templates currently require npm as they come with `package-lock.json` files.
99
+
54
100
  #### Package Managers
55
101
 
56
102
  With `create-gasket-app`, you can choose either [npm] or [yarn] as the package
@@ -6,6 +6,10 @@ import { rm } from 'fs/promises';
6
6
  import { makeGasket } from '@gasket/core';
7
7
  import globalPrompts from '../scaffold/actions/global-prompts.js';
8
8
  import loadPreset from '../scaffold/actions/load-preset.js';
9
+ import loadTemplate from '../scaffold/actions/load-template.js';
10
+ import copyTemplate from '../scaffold/actions/copy-template.js';
11
+ import customizeTemplate from '../scaffold/actions/customize-template.js';
12
+ import installTemplateDep from '../scaffold/actions/install-template-deps.js';
9
13
  import presetPromptHooks from '../scaffold/actions/preset-prompt-hooks.js';
10
14
  import presetConfigHooks from '../scaffold/actions/preset-config-hooks.js';
11
15
  import promptHooks from '../scaffold/actions/prompt-hooks.js';
@@ -45,6 +49,17 @@ const createCommand = {
45
49
  Or other (multiple) custom presets (e.g. --presets my-gasket-preset@1.0.0.beta-1,nextjs@^1.0.0)`,
46
50
  parse: commasToArray
47
51
  },
52
+ {
53
+ name: 'template',
54
+ description: `Selects which template you would like to use during
55
+ installation. (e.g. --template @gasket/template-nextjs-pages-js)`
56
+ },
57
+ {
58
+ name: 'template-path',
59
+ description: `(INTERNAL) Path to a local template package. Can be absolute
60
+ or relative to the current working directory.`,
61
+ hidden: true
62
+ },
48
63
  {
49
64
  name: 'package-manager',
50
65
  description: `Selects which package manager you would like to use during
@@ -90,6 +105,86 @@ const createCommand = {
90
105
  ]
91
106
  };
92
107
 
108
+ /**
109
+ * validateOption - We require at least one of the options to be provided
110
+ * @param {import('../internal.js').PartialCreateContext} context
111
+ * @returns {import('../internal.js').PartialCreateContext}
112
+ */
113
+ function validateOptions(context) {
114
+ const hasOption = (context) => Boolean(context.template || context.templatePath || context.rawPresets.length || context.localPresets.length);
115
+
116
+ if (!hasOption(context)) {
117
+ console.warn('Warning: At least one of the options is required: --template, --template-path, --presets, --preset-path');
118
+ process.exit(1);
119
+ }
120
+
121
+ return context;
122
+ }
123
+
124
+ /**
125
+ * handleTemplate - Handles the template creation
126
+ * @param {import('../internal.js').PartialCreateContext} context
127
+ * @returns {Promise<void>}
128
+ */
129
+ async function handleTemplate(context) {
130
+ await mkDir({ context });
131
+ await loadTemplate({ context });
132
+ await copyTemplate({ context });
133
+ await customizeTemplate({ context });
134
+ await installTemplateDep({ context });
135
+ printReport({ context });
136
+ return;
137
+ }
138
+
139
+ /**
140
+ * handlePresets - Handles the preset creation
141
+ * @param {import('../internal.js').PartialCreateContext} context
142
+ * @returns {Promise<void>}
143
+ */
144
+ async function handlePresets(context) {
145
+ await loadPreset({ context });
146
+ await globalPrompts({ context });
147
+
148
+ const presetGasket = makeGasket({
149
+ plugins: context.presets
150
+ });
151
+
152
+ await presetPromptHooks({ gasket: presetGasket, context });
153
+ await presetConfigHooks({ gasket: presetGasket, context });
154
+
155
+ const pluginGasket = makeGasket({
156
+ ...context.presetConfig,
157
+ plugins: context.presetConfig.plugins.concat(context.presets)
158
+ });
159
+
160
+ await promptHooks({ gasket: pluginGasket, context });
161
+ await mkDir({ context });
162
+ await setupPkg({ context });
163
+ await createHooks({ gasket: pluginGasket, context });
164
+ await writePkg({ context });
165
+ await generateFiles({ context });
166
+ await writeGasketConfig({ context });
167
+ await installModules({ context });
168
+ await linkModules({ context });
169
+ await postCreateHooks({ gasket: pluginGasket, context });
170
+ if (context.tmpDir) await rm(context.tmpDir, { recursive: true });
171
+ printReport({ context });
172
+ }
173
+
174
+ /**
175
+ * handleCreate - Handles the create creation
176
+ * @param {import('../internal.js').PartialCreateContext} context
177
+ * @returns {Promise<void>}
178
+ */
179
+ async function handleCreate(context) {
180
+ const { template, templatePath } = context;
181
+ if (template || templatePath) {
182
+ await handleTemplate(context);
183
+ } else {
184
+ await handlePresets(context);
185
+ }
186
+ }
187
+
93
188
  /**
94
189
  * createCommand action
95
190
  * @type {import('../index.js').createCommandAction}
@@ -98,39 +193,10 @@ createCommand.action = async function run(appname, options, command) {
98
193
  // eslint-disable-next-line no-process-env
99
194
  process.env.GASKET_ENV = 'create';
100
195
  const context = makeCreateContext([appname], options);
101
- const { rawPresets, localPresets } = context;
196
+ validateOptions(context);
102
197
 
103
198
  try {
104
- await globalPrompts({ context });
105
-
106
- if (rawPresets.length || localPresets.length) {
107
- await loadPreset({ context });
108
-
109
- const presetGasket = makeGasket({
110
- plugins: context.presets
111
- });
112
-
113
- await presetPromptHooks({ gasket: presetGasket, context });
114
- await presetConfigHooks({ gasket: presetGasket, context });
115
- }
116
-
117
- const pluginGasket = makeGasket({
118
- ...context.presetConfig,
119
- plugins: context.presetConfig.plugins.concat(context.presets)
120
- });
121
-
122
- await promptHooks({ gasket: pluginGasket, context });
123
- await mkDir({ context });
124
- await setupPkg({ context });
125
- await createHooks({ gasket: pluginGasket, context });
126
- await writePkg({ context });
127
- await generateFiles({ context });
128
- await writeGasketConfig({ context });
129
- await installModules({ context });
130
- await linkModules({ context });
131
- await postCreateHooks({ gasket: pluginGasket, context });
132
- if (context.tmpDir) await rm(context.tmpDir, { recursive: true });
133
- printReport({ context });
199
+ await handleCreate(context);
134
200
  } catch (err) {
135
201
  // eslint-disable-next-line no-console
136
202
  console.error(chalk.red('Exiting with errors.'));
package/lib/index.d.ts CHANGED
@@ -47,6 +47,8 @@ interface CommandOption {
47
47
 
48
48
  export interface CreateCommandOptions {
49
49
  presets?: string[];
50
+ template?: string;
51
+ templatePath?: string;
50
52
  npmLink?: string[];
51
53
  presetPath?: string[];
52
54
  packageManager?: string;
@@ -459,6 +461,18 @@ export interface CreateContext {
459
461
  /** temporary directory */
460
462
  tmpDir: string;
461
463
 
464
+ /** template to use for app creation */
465
+ template?: string;
466
+
467
+ /** path to local template package */
468
+ templatePath?: string;
469
+
470
+ /** resolved template directory path */
471
+ templateDir?: string;
472
+
473
+ /** resolved template name for display */
474
+ templateName?: string;
475
+
462
476
  /** Default to object w/empty plugins array to be populated by `presetConfig` hook */
463
477
  presetConfig: GasketConfigDefinition;
464
478
 
@@ -505,7 +519,7 @@ export interface CreateContext {
505
519
 
506
520
  /** Use to add content to the README.md */
507
521
  readme: Readme;
508
-
522
+ // eslint-disable-next-line @typescript-eslint/no-misused-new
509
523
  constructor(initContext?: Partial<CreateContext>);
510
524
  runWith(plugin: Plugin): CreateContext;
511
525
  /** Flag indicating if typescript is enabled */
package/lib/index.js CHANGED
@@ -17,7 +17,10 @@ const gasketBin = program
17
17
  .version(pkg.version)
18
18
  .addHelpText('beforeAll', logo);
19
19
 
20
-
20
+ /**
21
+ * The main entry point for the create-gasket-app CLI.
22
+ * @returns {Promise<Command>} promise
23
+ */
21
24
  async function main() {
22
25
  // Suppress deprecation warning for punycode in node 22
23
26
  process.noDeprecation = true;
@@ -30,4 +33,4 @@ async function main() {
30
33
  return await gasketBin.parseAsync();
31
34
  }
32
35
 
33
- main();
36
+ void main();
package/lib/internal.d.ts CHANGED
@@ -104,6 +104,7 @@ export function promptForTestPlugin(
104
104
  export function allowExtantOverwriting(context: PartialCreateContext, prompt: CreatePrompt): Promise<void>;
105
105
  export function globalPrompts(params: { context: PartialCreateContext }): Promise<void>;
106
106
  export function loadPresets(params: { context: PartialCreateContext }): Promise<void>;
107
+
107
108
  export function presetPromptHooks(params: { gasket?: Gasket; context: PartialCreateContext }): Promise<void>;
108
109
  export function presetConfigHooks(params: { gasket?: Gasket; context: PartialCreateContext }): Promise<void>;
109
110
  export function promptHooks(params: { gasket?: Gasket; context: PartialCreateContext }): Promise<void>;
@@ -124,6 +125,15 @@ export type Descriptior = {
124
125
  from: string;
125
126
  overrides?: string;
126
127
  }
128
+
129
+ /** Templates */
130
+ export function loadTemplate(params: { context: PartialCreateContext }): Promise<void>;
131
+ export function copyTemplate(params: { context: PartialCreateContext }): Promise<void>;
132
+ export function customizeTemplate(params: { context: PartialCreateContext }): Promise<void>;
133
+ export function installTemplateDeps(params: { context: PartialCreateContext }): Promise<void>;
134
+
135
+
136
+ /** Presets */
127
137
  export function generateFiles({ context, spinner }: { context: CreateContext, spinner: Ora }): Promise<void>;
128
138
  export function performGenerate(context: CreateContext, descriptors: Descriptior[]): boolean[];
129
139
  export function getDescriptors(context: CreateContext): Promise<Descriptior[]>;
@@ -0,0 +1,22 @@
1
+ import path from 'path';
2
+ import { withSpinner } from '../with-spinner.js';
3
+ import { cp, readdir } from 'fs/promises';
4
+
5
+ const EXCLUDED_FILES = ['node_modules'];
6
+
7
+ /** @type {import('../../internal.d.ts').copyTemplate} */
8
+ async function copyTemplate({ context }) {
9
+ if (!context.templateDir) return;
10
+
11
+ const entries = await readdir(context.templateDir, { withFileTypes: true });
12
+ const filesToCopy = entries.filter(entry => !EXCLUDED_FILES.includes(entry.name));
13
+
14
+ for (const entry of filesToCopy) {
15
+ const sourcePath = path.join(context.templateDir, entry.name);
16
+ const destPath = path.join(context.dest, entry.name);
17
+ await cp(sourcePath, destPath, { recursive: true });
18
+ context.generatedFiles.add(path.relative(context.cwd, destPath));
19
+ }
20
+ }
21
+
22
+ export default withSpinner('Copy template files', copyTemplate);
@@ -0,0 +1,92 @@
1
+ import path from 'path';
2
+ import { withSpinner } from '../with-spinner.js';
3
+ import { readFile, writeFile } from 'fs/promises';
4
+
5
+ /** @type {import('../../internal.d.ts').customizeTemplate} */
6
+ async function customizeTemplate({ context }) {
7
+ if (!context.templateDir || !context.appName) return;
8
+
9
+ await Promise.all([
10
+ updatePackageJson(context),
11
+ updateReadme(context),
12
+ updateTemplateFiles(context)
13
+ ]);
14
+ }
15
+
16
+ /**
17
+ * Updates package.json name field with app name
18
+ * @param {object} context - Gasket context
19
+ * @returns {Promise<void>}
20
+ */
21
+ async function updatePackageJson(context) {
22
+ const packageJsonPath = path.join(context.dest, 'package.json');
23
+
24
+ try {
25
+ const content = await readFile(packageJsonPath, 'utf8');
26
+ const packageJson = JSON.parse(content);
27
+
28
+ packageJson.name = context.appName;
29
+
30
+ await writeFile(packageJsonPath, JSON.stringify(packageJson, null, 2) + '\n');
31
+ } catch (error) {
32
+ // Ignore if package.json doesn't exist or is malformed
33
+ if (error.code !== 'ENOENT') {
34
+ console.warn(`Warning: Could not update package.json: ${error.message}`);
35
+ }
36
+ }
37
+ }
38
+
39
+ /**
40
+ * Updates README.md with app name placeholders replaced
41
+ * @param {object} context - Gasket context
42
+ * @returns {Promise<void>}
43
+ */
44
+ async function updateReadme(context) {
45
+ const readmePath = path.join(context.dest, 'README.md');
46
+
47
+ try {
48
+ const content = await readFile(readmePath, 'utf8');
49
+
50
+ // Replace all {appName} placeholders with actual app name
51
+ const updatedContent = content.replace(/{{{appName}}}/g, context.appName);
52
+
53
+ await writeFile(readmePath, updatedContent);
54
+ } catch (error) {
55
+ // Ignore if README.md doesn't exist
56
+ if (error.code !== 'ENOENT') {
57
+ console.warn(`Warning: Could not update README.md: ${error.message}`);
58
+ }
59
+ }
60
+ }
61
+
62
+ /**
63
+ * Updates template files with app name placeholders replaced
64
+ * @param {object} context - Gasket context
65
+ * @returns {Promise<void>}
66
+ */
67
+ async function updateTemplateFiles(context) {
68
+ const templateFilePaths = [
69
+ path.join(context.dest, 'pages/index.tsx'),
70
+ path.join(context.dest, 'app/page.tsx')
71
+ ];
72
+
73
+ const updatePromises = templateFilePaths.map(async (filePath) => {
74
+ try {
75
+ const content = await readFile(filePath, 'utf8');
76
+
77
+ // Replace all {appName} placeholders with actual app name
78
+ const updatedContent = content.replace(/{{{appName}}}/g, context.appName);
79
+
80
+ await writeFile(filePath, updatedContent);
81
+ } catch (error) {
82
+ // Ignore if file doesn't exist (not all templates have these files)
83
+ if (error.code !== 'ENOENT') {
84
+ console.warn(`Warning: Could not update ${filePath}: ${error.message}`);
85
+ }
86
+ }
87
+ });
88
+
89
+ await Promise.all(updatePromises);
90
+ }
91
+
92
+ export default withSpinner('Customize template files', customizeTemplate);
@@ -0,0 +1,61 @@
1
+ import { withSpinner } from '../with-spinner.js';
2
+ import { PackageManager } from '@gasket/utils';
3
+ import { rm } from 'fs/promises';
4
+
5
+ /**
6
+ * Safely cleans up temporary directory
7
+ * @param {string} tmpDir - Temporary directory path
8
+ * @returns {Promise<void>}
9
+ */
10
+ async function cleanupTempDir(tmpDir) {
11
+ if (!tmpDir) return;
12
+
13
+ try {
14
+ await rm(tmpDir, { recursive: true });
15
+ } catch {
16
+ // Ignore cleanup errors
17
+ }
18
+ }
19
+
20
+ /**
21
+ * Checks if an error is related to peer dependencies
22
+ * @param {Error & { stderr?: string }} error - The error to check
23
+ * @returns {boolean} True if error is peer dep related
24
+ */
25
+ function isPeerDependencyError(error) {
26
+ const errorMessage = (error.stderr || error.message || '').toLowerCase();
27
+ return errorMessage.includes('peer dep') ||
28
+ errorMessage.includes('peerinvalid') ||
29
+ errorMessage.includes('peer dependencies') ||
30
+ errorMessage.includes('eresolve');
31
+ }
32
+
33
+ /** @type {import('../../internal.d.ts').installTemplateDeps} */
34
+ async function installTemplateDeps({ context }) {
35
+ const destPkgManager = new PackageManager({
36
+ packageManager: 'npm',
37
+ dest: context.dest
38
+ });
39
+
40
+ try {
41
+ await destPkgManager.exec('ci');
42
+ await cleanupTempDir(context.tmpDir);
43
+ } catch (error) {
44
+ if (isPeerDependencyError(error)) {
45
+ console.warn('Peer dependency conflict detected, retrying with --legacy-peer-deps...');
46
+ try {
47
+ await destPkgManager.exec('ci', ['--legacy-peer-deps']);
48
+ await cleanupTempDir(context.tmpDir);
49
+ context.messages.push('Dependencies were installed with --legacy-peer-deps due to confilct');
50
+ } catch (retryError) {
51
+ await cleanupTempDir(context.tmpDir);
52
+ throw new Error(`Failed to install dependencies even with --legacy-peer-deps: ${retryError.message}`);
53
+ }
54
+ } else {
55
+ await cleanupTempDir(context.tmpDir);
56
+ throw error;
57
+ }
58
+ }
59
+ }
60
+
61
+ export default withSpinner('Install template dependencies', installTemplateDeps);
@@ -0,0 +1,110 @@
1
+ import path from 'path';
2
+ import os from 'os';
3
+ import { withSpinner } from '../with-spinner.js';
4
+ import { PackageManager } from '@gasket/utils';
5
+ import { mkdtemp, rm } from 'fs/promises';
6
+
7
+ /**
8
+ * Parses template string into name and version components
9
+ * @param {string} template - Template string (e.g., "@gasket/template-api@1.0.0")
10
+ * @returns {{ name: string, version: string }} Parsed template name and version
11
+ */
12
+ function parseTemplateNameAndVersion(template) {
13
+ // capture name and version, excluding `@`
14
+ const reNameVersion = /(^.[^@]+)[@](.+$)/i;
15
+ const [
16
+ name,
17
+ version = 'latest'
18
+ ] = template.split(reNameVersion).filter(Boolean);
19
+
20
+ return {
21
+ name,
22
+ version
23
+ };
24
+ }
25
+
26
+ /**
27
+ * Safely cleans up temporary directory
28
+ * @param {string} tmpDir - Temporary directory path
29
+ * @returns {Promise<void>}
30
+ */
31
+ async function cleanupTempDir(tmpDir) {
32
+ if (!tmpDir) return;
33
+
34
+ try {
35
+ await rm(tmpDir, { recursive: true });
36
+ } catch {
37
+ // Ignore cleanup errors
38
+ }
39
+ }
40
+
41
+ /**
42
+ * Downloads and installs a remote template package
43
+ * @param {string} template - Template name with optional version
44
+ * @param {string} appName - App name for temp directory
45
+ * @returns {Promise<{ templateDir: string, templateName: string, tmpDir: string }>} Template installation details
46
+ */
47
+ async function installRemoteTemplate(template, appName) {
48
+ const tmpDir = await mkdtemp(path.join(os.tmpdir(), `gasket-template-${appName}`));
49
+ const modPath = path.join(tmpDir, 'node_modules');
50
+ const pkgManager = new PackageManager({
51
+ packageManager: 'npm',
52
+ dest: tmpDir
53
+ });
54
+
55
+ const { name, version } = parseTemplateNameAndVersion(template);
56
+
57
+ try {
58
+ await pkgManager.exec('install', [`${name}@${version}`]);
59
+ return {
60
+ templateDir: path.join(modPath, name, 'template'),
61
+ templateName: `${name}@${version}`,
62
+ tmpDir
63
+ };
64
+ } catch (err) {
65
+ await cleanupTempDir(tmpDir);
66
+
67
+ const errorMessage = err.stderr || err.message;
68
+ if (err.stderr?.includes('is not in this registry')) {
69
+ const message = `Template not found in registry: ${name}@${version}. ` +
70
+ 'Use npm_config_registry=<registry> to use privately scoped templates.';
71
+ throw new Error(message);
72
+ }
73
+ throw new Error(`Failed to install template ${name}@${version}: ${errorMessage}`);
74
+ }
75
+ }
76
+
77
+ /**
78
+ * Gets template directory and name based on context
79
+ * @param {object} context - Gasket creation context
80
+ * @returns {Promise<{ templateDir: string, templateName: string, tmpDir?: string }>} Template details
81
+ */
82
+ async function getTemplateDetails(context) {
83
+ if (context.templatePath) {
84
+ return {
85
+ templateDir: path.resolve(context.templatePath, 'template'),
86
+ templateName: `local template at ${path.resolve(context.templatePath, 'template')}`
87
+ };
88
+ }
89
+
90
+ const result = await installRemoteTemplate(context.template, context.appName);
91
+ return {
92
+ templateDir: result.templateDir,
93
+ templateName: result.templateName,
94
+ tmpDir: result.tmpDir
95
+ };
96
+ }
97
+
98
+ /** @type {import('../../internal.d.ts').loadTemplate} */
99
+ async function loadTemplate({ context }) {
100
+ if (!context.template && !context.templatePath) return;
101
+
102
+ const { templateDir, templateName, tmpDir } = await getTemplateDetails(context);
103
+
104
+ // Store template information in context for other actions to use
105
+ context.templateDir = templateDir;
106
+ context.templateName = templateName;
107
+ context.tmpDir = tmpDir;
108
+ }
109
+
110
+ export default withSpinner('Loading template', loadTemplate);
@@ -1,4 +1,4 @@
1
- /* eslint-disable max-len, max-statements */
1
+ /* eslint-disable max-statements */
2
2
  import fs from 'fs';
3
3
  import path from 'path';
4
4
  import { readConfig } from '../scaffold/utils.js';
@@ -53,9 +53,10 @@ function makeCreateRuntime(context, source) {
53
53
  });
54
54
  }
55
55
 
56
- function flatten(acc, values) {
56
+
57
+ const flatten = (acc, values) => {
57
58
  return acc.concat(values);
58
- }
59
+ };
59
60
 
60
61
  export class CreateContext {
61
62
  constructor(initContext = {}) {
@@ -73,6 +74,8 @@ export function makeCreateContext(argv = [], options = {}) {
73
74
  const appName = argv[0] || 'templated-app';
74
75
  const {
75
76
  presets = [],
77
+ template,
78
+ templatePath,
76
79
  npmLink = [],
77
80
  presetPath = [],
78
81
  packageManager,
@@ -106,6 +109,8 @@ export function makeCreateContext(argv = [], options = {}) {
106
109
  pkgLinks,
107
110
  localPresets,
108
111
  rawPresets,
112
+ template,
113
+ templatePath,
109
114
  messages: [],
110
115
  warnings: [],
111
116
  errors: [],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-gasket-app",
3
- "version": "7.4.11",
3
+ "version": "7.4.13",
4
4
  "description": "starter pack for creating a gasket app",
5
5
  "type": "module",
6
6
  "bin": {
@@ -9,7 +9,8 @@
9
9
  "main": "lib/index.js",
10
10
  "types": "lib/index.d.ts",
11
11
  "files": [
12
- "lib"
12
+ "lib",
13
+ "EXAMPLES.md"
13
14
  ],
14
15
  "repository": "godaddy/gasket.git",
15
16
  "publishConfig": {
@@ -31,33 +32,31 @@
31
32
  "mkdirp": "^1.0.4",
32
33
  "ora": "^3.4.0",
33
34
  "semver": "^7.7.1",
34
- "@gasket/plugin-command": "^7.5.1",
35
- "@gasket/plugin-docs": "^7.4.6",
36
- "@gasket/plugin-dynamic-plugins": "^7.4.1",
37
- "@gasket/core": "^7.6.1",
38
- "@gasket/plugin-docusaurus": "^7.4.5",
39
- "@gasket/plugin-git": "^7.4.5",
40
- "@gasket/plugin-logger": "^7.3.6",
41
- "@gasket/request": "^7.5.2",
42
- "@gasket/plugin-metadata": "^7.5.2",
43
- "@gasket/utils": "^7.6.1"
35
+ "@gasket/core": "^7.6.3",
36
+ "@gasket/plugin-command": "^7.5.3",
37
+ "@gasket/plugin-docs": "^7.4.8",
38
+ "@gasket/plugin-docusaurus": "^7.4.6",
39
+ "@gasket/plugin-dynamic-plugins": "^7.4.2",
40
+ "@gasket/plugin-git": "^7.4.7",
41
+ "@gasket/plugin-logger": "^7.3.8",
42
+ "@gasket/plugin-metadata": "^7.5.4",
43
+ "@gasket/request": "^7.5.4",
44
+ "@gasket/utils": "^7.6.3"
44
45
  },
45
46
  "devDependencies": {
46
47
  "@typescript-eslint/parser": "^8.38.0",
48
+ "@vitest/coverage-v8": "^3.2.0",
47
49
  "dotenv": "^16.4.7",
48
- "jest": "^29.7.0",
49
- "@gasket/request": "^7.5.2"
50
- },
51
- "jest": {
52
- "transform": {}
50
+ "vitest": "^3.2.0",
51
+ "@gasket/request": "^7.5.4"
53
52
  },
54
53
  "scripts": {
55
54
  "lint": "eslint .",
56
55
  "lint:fix": "pnpm run lint --fix",
57
56
  "posttest": "pnpm run lint && pnpm run typecheck",
58
- "test": "cross-env NODE_OPTIONS='--unhandled-rejections=strict --experimental-vm-modules' jest",
59
- "test:coverage": "jest --coverage",
60
- "test:watch": "pnpm run test --watch",
57
+ "test": "vitest run --globals",
58
+ "test:coverage": "vitest run --coverage",
59
+ "test:watch": "vitest",
61
60
  "typecheck": "tsc",
62
61
  "typecheck:watch": "tsc --watch"
63
62
  }