create-jst 0.4.3 → 0.4.8

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-jst",
3
- "version": "0.4.3",
3
+ "version": "0.4.8",
4
4
  "description": "Create a web application from the JST template.",
5
5
  "type": "module",
6
6
  "packageManager": "npm@11.6.2",
@@ -4,6 +4,7 @@ export function createProjectInitializer({ onProgress = () => undefined, project
4
4
  await projectGateway.assertDestinationIsEmpty(specification.destination)
5
5
  const temporaryDestination = await projectGateway.prepareDestination(specification.destination)
6
6
  const temporarySpecification = Object.freeze({ ...specification, destination: temporaryDestination })
7
+ let rollbackDestination = temporaryDestination
7
8
 
8
9
  try {
9
10
  onProgress({ type: 'template.download.started', specification })
@@ -28,16 +29,17 @@ export function createProjectInitializer({ onProgress = () => undefined, project
28
29
  onProgress({ type: 'git.initialization.started', specification })
29
30
  await projectGateway.initializeGit(temporaryDestination)
30
31
  }
32
+ await projectGateway.commitDestination(temporaryDestination, specification.destination)
33
+ rollbackDestination = specification.destination
31
34
  if (specification.install) {
32
35
  onProgress({ type: 'dependencies.installation.started', specification })
33
- await projectGateway.installDependencies(temporaryDestination, specification.packageManager)
36
+ await projectGateway.installDependencies(specification.destination, specification.packageManager)
34
37
  }
35
38
 
36
- await projectGateway.commitDestination(temporaryDestination, specification.destination)
37
39
  onProgress({ type: 'project.initialized', specification })
38
40
  }
39
41
  catch (error) {
40
- await projectGateway.cleanupDestination(temporaryDestination)
42
+ await projectGateway.cleanupDestination(rollbackDestination)
41
43
  throw error
42
44
  }
43
45
  },
@@ -3,7 +3,7 @@ export const PRIMARY_COLORS = ['dark', 'gray', 'red', 'pink', 'grape', 'violet',
3
3
  export const PACKAGE_MANAGERS = ['npm', 'pnpm']
4
4
  export const STYLE_LANGUAGES = ['css', 'scss']
5
5
  export const EXAMPLES = ['clean', 'showcase']
6
- export const TEMPLATE_SOURCE = 'jst-stack/jst#v0.4.2'
6
+ export const TEMPLATE_SOURCE = 'jst-stack/jst#v0.4.3'
7
7
  export const SHOWCASE_SOURCE = 'jst-stack/jst-showcase#d3625bda10ddb9c39599cc061e3f49cdb6be3922'
8
8
 
9
9
  export function buildProjectSpecification(input) {
@@ -1,6 +1,7 @@
1
1
  import { execFile } from 'node:child_process'
2
2
  import { readFile, rm, writeFile } from 'node:fs/promises'
3
3
  import { join } from 'node:path'
4
+ import process from 'node:process'
4
5
  import { promisify } from 'node:util'
5
6
 
6
7
  const executeFile = promisify(execFile)
@@ -32,7 +33,10 @@ export function createNodeMigrationGateway() {
32
33
  async verify(directory) {
33
34
  const manifest = JSON.parse(await readFile(join(directory, 'package.json'), 'utf8'))
34
35
  const packageManager = manifest.packageManager?.startsWith('pnpm@') ? 'pnpm' : 'npm'
35
- return executeFile(packageManager, ['run', 'check'], { cwd: directory })
36
+ return executeFile(packageManager, ['run', 'check'], {
37
+ cwd: directory,
38
+ shell: process.platform === 'win32',
39
+ })
36
40
  },
37
41
  async writeTransaction(directory, files, verify) {
38
42
  const originals = new Map()
@@ -1,7 +1,7 @@
1
1
  import { spawn, execFile } from 'node:child_process'
2
2
  import { existsSync } from 'node:fs'
3
3
  import { lstat, readFile, readdir, rename, rm, writeFile } from 'node:fs/promises'
4
- import { basename, dirname, join } from 'node:path'
4
+ import { basename, dirname, extname, join } from 'node:path'
5
5
  import process from 'node:process'
6
6
  import { promisify } from 'node:util'
7
7
 
@@ -10,6 +10,8 @@ const installCommands = {
10
10
  npm: ['install', '--no-audit', '--no-fund'],
11
11
  pnpm: ['install'],
12
12
  }
13
+ const pnpmAction = 'pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0'
14
+ const textExtensions = new Set(['.md', '.mjs', '.yaml', '.yml'])
13
15
 
14
16
  export function createNodeProjectGateway({ nodeExecutable, signal }) {
15
17
  return {
@@ -58,7 +60,7 @@ export function createNodeProjectGateway({ nodeExecutable, signal }) {
58
60
  '--color-scheme', specification.colorScheme,
59
61
  '--primary-color', specification.primaryColor,
60
62
  '--style', specification.style,
61
- ], specification.destination, signal)
63
+ ], specification.destination, { signal })
62
64
  }
63
65
 
64
66
  async function configureExample(specification) {
@@ -69,11 +71,14 @@ export function createNodeProjectGateway({ nodeExecutable, signal }) {
69
71
  }
70
72
 
71
73
  function initializeGit(destination) {
72
- return run('git', ['init', '--initial-branch=main'], destination, signal)
74
+ return run('git', ['init', '--initial-branch=main'], destination, { signal })
73
75
  }
74
76
 
75
77
  function installDependencies(destination, packageManager) {
76
- return run(packageManager, installCommands[packageManager], destination, signal)
78
+ return run(packageManager, installCommands[packageManager], destination, {
79
+ shell: process.platform === 'win32',
80
+ signal,
81
+ })
77
82
  }
78
83
 
79
84
  async function validateTemplate(specification) {
@@ -112,6 +117,9 @@ async function configurePackageManager(destination, packageManager, signal) {
112
117
  catch {
113
118
  throw new Error(`Could not run ${packageManager}. Install it or choose another package manager.`)
114
119
  }
120
+ if (packageManager === 'pnpm') {
121
+ manifest.scripts = replaceManifestCommands(manifest.scripts)
122
+ }
115
123
  const packageManagerEntry = ['packageManager', `${packageManager}@${version.trim()}`]
116
124
  const entries = Object.entries(manifest).flatMap(entry => entry[0] === 'engines' ? [packageManagerEntry, entry] : [entry])
117
125
  if (!manifest.engines) {
@@ -129,34 +137,67 @@ minimumReleaseAgeExclude:
129
137
  trustPolicy: no-downgrade
130
138
  trustPolicyIgnoreAfter: 10080
131
139
  shellEmulator: true
140
+
141
+ packages:
142
+ - .
132
143
  `,
133
144
  )
134
- await replaceReadmeCommands(destination, 'pnpm')
145
+ await replaceProjectCommands(destination)
135
146
  }
136
147
  }
137
148
 
138
- async function replaceReadmeCommands(destination, packageManager) {
139
- const path = join(destination, 'README.md')
140
- try {
141
- const readme = await readFile(path, 'utf8')
142
- await writeFile(path, readme.replaceAll('npm run ', `${packageManager} run `).replaceAll('npm ci', `${packageManager} install --frozen-lockfile`))
143
- }
144
- catch (error) {
145
- if (error?.code !== 'ENOENT') {
146
- throw error
149
+ function replaceManifestCommands(scripts = {}) {
150
+ return Object.fromEntries(Object.entries(scripts).map(([name, command]) => [name, replacePnpmCommands(command)]))
151
+ }
152
+
153
+ async function replaceProjectCommands(directory) {
154
+ for (const entry of await readdir(directory, { withFileTypes: true })) {
155
+ const path = join(directory, entry.name)
156
+ if (entry.isDirectory()) {
157
+ await replaceProjectCommands(path)
158
+ }
159
+ else if (textExtensions.has(extname(entry.name)) || entry.name === 'Dockerfile') {
160
+ const source = await readFile(path, 'utf8')
161
+ const converted = entry.name === 'Dockerfile' ? replacePnpmDockerfile(source) : replacePnpmText(source)
162
+ if (converted !== source) {
163
+ await writeFile(path, converted)
164
+ }
147
165
  }
148
166
  }
149
167
  }
150
168
 
169
+ function replacePnpmText(source) {
170
+ let result = replacePnpmCommands(source)
171
+ result = result.replaceAll('cache: npm', 'cache: pnpm')
172
+ result = result.replace(/^([ \t]*)- uses: actions\/setup-node@/mu, `$1- uses: ${pnpmAction}\n$&`)
173
+ return result.replace(/^\s*- run: npm install -g npm@[^\n]+\n/mu, '')
174
+ }
175
+
176
+ function replacePnpmCommands(source) {
177
+ return source
178
+ .replaceAll('npm ci', 'pnpm install --frozen-lockfile')
179
+ .replaceAll('npm run ', 'pnpm run ')
180
+ .replaceAll('npx ', 'pnpm exec ')
181
+ }
182
+
183
+ function replacePnpmDockerfile(source) {
184
+ return replacePnpmCommands(source)
185
+ .replaceAll(/^(FROM [^\n]+)$/gmu, '$1\nRUN corepack enable')
186
+ .replaceAll('COPY package.json package-lock.json ./', 'COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./')
187
+ .replaceAll('pnpm install --frozen-lockfile --omit=dev', 'pnpm install --prod --frozen-lockfile')
188
+ .replaceAll('npm cache clean --force', 'pnpm store prune')
189
+ .replaceAll('CMD ["npm", "start"]', 'CMD ["pnpm", "start"]')
190
+ }
191
+
151
192
  async function prepareDestination(destination) {
152
193
  const temporaryDestination = join(dirname(destination), `.${basename(destination)}-${process.pid}-${Date.now()}.tmp`)
153
194
  await rm(temporaryDestination, { force: true, recursive: true })
154
195
  return temporaryDestination
155
196
  }
156
197
 
157
- function run(command, args, cwd, signal) {
198
+ function run(command, args, cwd, { shell = false, signal } = {}) {
158
199
  return new Promise((resolve, reject) => {
159
- const child = spawn(command, args, { cwd, signal, stdio: 'inherit' })
200
+ const child = spawn(command, args, { cwd, shell, signal, stdio: 'inherit' })
160
201
  child.once('error', error => reject(new Error(`Could not run ${command}: ${error.message}`)))
161
202
  child.once('close', code => {
162
203
  if (code === 0) {