From 172f8aa04a42320dfe5b1cb7d5d3792760fe989d Mon Sep 17 00:00:00 2001 From: David Dal Busco Date: Thu, 11 Apr 2024 08:34:06 +0200 Subject: [PATCH 01/13] chore: debug windows --- src/services/generate.services.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/services/generate.services.ts b/src/services/generate.services.ts index 0a37522..d078094 100644 --- a/src/services/generate.services.ts +++ b/src/services/generate.services.ts @@ -57,6 +57,8 @@ type PopulateInputFn = Omit; const populateFromCDN = async ({where, template, localDevelopment}: PopulateInputFn) => { const templatePath = getRelativeTemplatePath(template); + console.log("DEBUG ----> ", templatePath); + const {hostname} = new URL(JUNO_CDN_URL); const buffer = await downloadFromURL({ @@ -67,6 +69,8 @@ const populateFromCDN = async ({where, template, localDevelopment}: PopulateInpu } }); + console.log("DEBUG ----> ", buffer); + const uncompressedBuffer = await gunzipFile({source: buffer}); const files = await untarFile({source: uncompressedBuffer}); From 755b2e4399b936699fb7c4aa0f748b90758466e2 Mon Sep 17 00:00:00 2001 From: David Dal Busco Date: Thu, 11 Apr 2024 08:40:30 +0200 Subject: [PATCH 02/13] chore: write to debug --- src/services/generate.services.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/services/generate.services.ts b/src/services/generate.services.ts index d078094..28442f6 100644 --- a/src/services/generate.services.ts +++ b/src/services/generate.services.ts @@ -71,6 +71,9 @@ const populateFromCDN = async ({where, template, localDevelopment}: PopulateInpu console.log("DEBUG ----> ", buffer); + // TODO: just for test + await writeFile(join(process.cwd(), `${template.key}-${new Date().getTime()}.tar.gz`), buffer); + const uncompressedBuffer = await gunzipFile({source: buffer}); const files = await untarFile({source: uncompressedBuffer}); From 54541cc1783bd653e250060d60cc152b760504f8 Mon Sep 17 00:00:00 2001 From: David Dal Busco Date: Thu, 11 Apr 2024 08:47:03 +0200 Subject: [PATCH 03/13] chore: test length --- src/utils/download.utils.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/utils/download.utils.ts b/src/utils/download.utils.ts index 7548343..27ee1f4 100644 --- a/src/utils/download.utils.ts +++ b/src/utils/download.utils.ts @@ -9,11 +9,19 @@ export const downloadFromURL = async (url: string | RequestOptions): Promise data.push(chunk)); res.on('end', () => { - resolve(Buffer.concat(data)); + const buffer = Buffer.concat(data); + + if(expectedLength && buffer.length.toString() !== expectedLength) { + reject(new Error('Downloaded data size does not match the expected data size.')); + } else { + resolve(buffer); + } }); res.on('error', reject); }); From 31de78af67ee643ff8af62c74b44854a7412861e Mon Sep 17 00:00:00 2001 From: David Dal Busco Date: Thu, 11 Apr 2024 12:16:26 +0200 Subject: [PATCH 04/13] feat: download with identity --- src/services/generate.services.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/generate.services.ts b/src/services/generate.services.ts index 28442f6..a1d3f66 100644 --- a/src/services/generate.services.ts +++ b/src/services/generate.services.ts @@ -65,7 +65,7 @@ const populateFromCDN = async ({where, template, localDevelopment}: PopulateInpu hostname, path: `/${templatePath}.tar.gz`, headers: { - 'Accept-Encoding': 'gzip, deflate, br' + 'Accept-Encoding': 'identity' } }); From 10971a9fe241e9fdfd4f23f1e40ab4e25700d964 Mon Sep 17 00:00:00 2001 From: David Dal Busco Date: Thu, 11 Apr 2024 12:23:25 +0200 Subject: [PATCH 05/13] fix: slash --- src/services/generate.services.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/services/generate.services.ts b/src/services/generate.services.ts index a1d3f66..e984c0a 100644 --- a/src/services/generate.services.ts +++ b/src/services/generate.services.ts @@ -55,7 +55,7 @@ const populate = async ({gitHubAction, ...rest}: PopulateInput) => { type PopulateInputFn = Omit; const populateFromCDN = async ({where, template, localDevelopment}: PopulateInputFn) => { - const templatePath = getRelativeTemplatePath(template); + const templatePath = getRelativeTemplatePath(template).replace(/\\/g, '/'); console.log("DEBUG ----> ", templatePath); @@ -65,7 +65,7 @@ const populateFromCDN = async ({where, template, localDevelopment}: PopulateInpu hostname, path: `/${templatePath}.tar.gz`, headers: { - 'Accept-Encoding': 'identity' + 'Accept-Encoding': 'gzip, deflate, br' } }); From 66f9cade437a1d2aae80fb5878acd22da6867bc9 Mon Sep 17 00:00:00 2001 From: David Dal Busco Date: Thu, 11 Apr 2024 12:46:02 +0200 Subject: [PATCH 06/13] fix: npm list --- src/services/cli.services.ts | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/src/services/cli.services.ts b/src/services/cli.services.ts index 07a027b..08eaf5b 100644 --- a/src/services/cli.services.ts +++ b/src/services/cli.services.ts @@ -12,19 +12,24 @@ const detectCliAlreadyInstalled = async (): Promise => { return false; } - const args = - pm === 'yarn' ? ['global', 'list', '--depth=0'] : ['list', '--depth', '0', '--global']; + try { + const args = + pm === 'yarn' ? ['global', 'list', '--depth=0'] : ['list', '--depth', '0', '--global']; - let stdout = ''; + let stdout = ''; - await spawn({ - command: pm, - args, - stdout: (output: string) => (stdout += output), - silentOut: true - }); + await spawn({ + command: pm, + args, + stdout: (output: string) => (stdout += output), + silentOut: true + }); - return stdout.includes(CLI_PACKAGE); + return stdout.includes(CLI_PACKAGE); + } catch (_err: unknown) { + // According to our tests, on Windows, npm might just throw an error if there are no global lib installed yet. Therefore, we consider here an error as the CLI not being installed yet. + return false; + } }; const install = async () => { From 824aa5a3a5bc62b174cd02b32abd6160390bfc86 Mon Sep 17 00:00:00 2001 From: David Dal Busco Date: Thu, 11 Apr 2024 12:52:35 +0200 Subject: [PATCH 07/13] fix: npm cmd on windows --- src/services/cli.services.ts | 2 +- src/utils/pm.utils.ts | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/services/cli.services.ts b/src/services/cli.services.ts index 08eaf5b..0d0f940 100644 --- a/src/services/cli.services.ts +++ b/src/services/cli.services.ts @@ -8,7 +8,7 @@ const detectCliAlreadyInstalled = async (): Promise => { const pm = whichPMRuns(); // Bun does not support list. For simplicity reason, we return false given that next question is "just" asking if developer want to install the cli. - if (!['npm', 'pnpm', 'yarn'].includes(pm)) { + if (!['npm', 'npm.cmd', 'pnpm', 'yarn'].includes(pm)) { return false; } diff --git a/src/utils/pm.utils.ts b/src/utils/pm.utils.ts index 8f5020f..3eed3ed 100644 --- a/src/utils/pm.utils.ts +++ b/src/utils/pm.utils.ts @@ -1,6 +1,6 @@ import {isNullish} from '@junobuild/utils'; -export type PM = 'npm' | 'yarn' | 'pnpm' | 'bun'; +export type PM = 'npm' | 'npm.cmd' | 'yarn' | 'pnpm' | 'bun'; export const whichPMRuns = (): PM => { const { @@ -21,7 +21,7 @@ export const whichPMRuns = (): PM => { case 'bun': return 'bun'; default: - return 'npm'; + return npm(); } }; @@ -37,3 +37,6 @@ const pmFromUserAgent = (userAgent: string): {name: string; version: string} => version: pmSpec.substring(separatorPos + 1) }; }; + +// Windows requires npm.cmd +const npm = (): 'npm' | 'npm.cmd' => (/^win/.test(process.platform) ? 'npm.cmd' : 'npm'); From 12596b6e38f33486d5eda901ebe2cc943ac2205d Mon Sep 17 00:00:00 2001 From: David Dal Busco Date: Thu, 11 Apr 2024 12:58:09 +0200 Subject: [PATCH 08/13] feat: try shell true --- src/utils/cmd.utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/cmd.utils.ts b/src/utils/cmd.utils.ts index 25c8f86..4b58b95 100644 --- a/src/utils/cmd.utils.ts +++ b/src/utils/cmd.utils.ts @@ -20,7 +20,7 @@ export const spawn = async ({ silentErrors?: boolean; }): Promise => { return await new Promise((resolve, reject) => { - const process: ChildProcessWithoutNullStreams = spawnCommand(command, args); + const process: ChildProcessWithoutNullStreams = spawnCommand(command, args, { shell: true }); process.stdout.on('data', (data) => { if (stdout !== null && stdout !== undefined) { From 206b53b1904a145b768a3a3984fb41ffbb498abc Mon Sep 17 00:00:00 2001 From: David Dal Busco Date: Thu, 11 Apr 2024 13:12:09 +0200 Subject: [PATCH 09/13] feat: revert --- src/utils/pm.utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/pm.utils.ts b/src/utils/pm.utils.ts index 3eed3ed..1f0c58e 100644 --- a/src/utils/pm.utils.ts +++ b/src/utils/pm.utils.ts @@ -21,7 +21,7 @@ export const whichPMRuns = (): PM => { case 'bun': return 'bun'; default: - return npm(); + return "npm"; } }; From 6694ce55faf3c73cdc9699cb705ca44af9811763 Mon Sep 17 00:00:00 2001 From: David Dal Busco Date: Thu, 11 Apr 2024 13:19:05 +0200 Subject: [PATCH 10/13] chore: revert --- src/services/cli.services.ts | 2 +- src/utils/pm.utils.ts | 7 ++----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/services/cli.services.ts b/src/services/cli.services.ts index 0d0f940..08eaf5b 100644 --- a/src/services/cli.services.ts +++ b/src/services/cli.services.ts @@ -8,7 +8,7 @@ const detectCliAlreadyInstalled = async (): Promise => { const pm = whichPMRuns(); // Bun does not support list. For simplicity reason, we return false given that next question is "just" asking if developer want to install the cli. - if (!['npm', 'npm.cmd', 'pnpm', 'yarn'].includes(pm)) { + if (!['npm', 'pnpm', 'yarn'].includes(pm)) { return false; } diff --git a/src/utils/pm.utils.ts b/src/utils/pm.utils.ts index 1f0c58e..8f5020f 100644 --- a/src/utils/pm.utils.ts +++ b/src/utils/pm.utils.ts @@ -1,6 +1,6 @@ import {isNullish} from '@junobuild/utils'; -export type PM = 'npm' | 'npm.cmd' | 'yarn' | 'pnpm' | 'bun'; +export type PM = 'npm' | 'yarn' | 'pnpm' | 'bun'; export const whichPMRuns = (): PM => { const { @@ -21,7 +21,7 @@ export const whichPMRuns = (): PM => { case 'bun': return 'bun'; default: - return "npm"; + return 'npm'; } }; @@ -37,6 +37,3 @@ const pmFromUserAgent = (userAgent: string): {name: string; version: string} => version: pmSpec.substring(separatorPos + 1) }; }; - -// Windows requires npm.cmd -const npm = (): 'npm' | 'npm.cmd' => (/^win/.test(process.platform) ? 'npm.cmd' : 'npm'); From b26e24c6ff890965f9c0c1465712c33f45a5627d Mon Sep 17 00:00:00 2001 From: David Dal Busco Date: Thu, 11 Apr 2024 13:31:01 +0200 Subject: [PATCH 11/13] feat: dependencies in folder --- src/services/deps.services.ts | 11 +++++++++-- src/services/generate.services.ts | 15 ++------------- src/services/project.services.ts | 15 ++++++++++++--- src/types/generator.ts | 4 ++++ 4 files changed, 27 insertions(+), 18 deletions(-) diff --git a/src/services/deps.services.ts b/src/services/deps.services.ts index 508ccd5..0919cf6 100644 --- a/src/services/deps.services.ts +++ b/src/services/deps.services.ts @@ -1,9 +1,11 @@ +import {nonNullish} from '@junobuild/utils'; import ora from 'ora'; +import type {PopulateInput} from '../types/generator'; import {spawn} from '../utils/cmd.utils'; import {whichPMRuns} from '../utils/pm.utils'; import {confirm} from '../utils/prompts.utils'; -export const dependencies = async () => { +export const dependencies = async ({where}: PopulateInput) => { const install = await confirm('Install dependencies?'); if (!install) { @@ -15,9 +17,14 @@ export const dependencies = async () => { try { const pm = whichPMRuns(); + const args = [ + 'install', + ...(nonNullish(where) && where !== '' ? ['--prefix', where ?? ''] : []) + ]; + await spawn({ command: pm, - args: ['install'], + args, silentOut: true }); } finally { diff --git a/src/services/generate.services.ts b/src/services/generate.services.ts index 9ed6440..29ad191 100644 --- a/src/services/generate.services.ts +++ b/src/services/generate.services.ts @@ -5,7 +5,7 @@ import {basename, join, parse} from 'node:path'; import ora from 'ora'; import {JUNO_CDN_URL} from '../constants/constants'; import {GITHUB_ACTION_DEPLOY} from '../templates/github-actions'; -import type {GeneratorInput} from '../types/generator'; +import type {GeneratorInput, PopulateInput} from '../types/generator'; import {gunzipFile, untarFile, type UntarOutputFile} from '../utils/compress.utils'; import {downloadFromURL} from '../utils/download.utils'; import { @@ -15,18 +15,7 @@ import { } from '../utils/fs.utils'; import {createDirectory, getLocalFiles, type LocalFileDescriptor} from '../utils/populate.utils'; -type PopulateInput = { - where: string | null; -} & Omit; - -export const generate = async ({destination, ...rest}: GeneratorInput) => { - await populate({ - where: ['.', ''].includes(destination) ? null : destination, - ...rest - }); -}; - -const populate = async ({gitHubAction, ...rest}: PopulateInput) => { +export const generate = async ({gitHubAction, ...rest}: PopulateInput) => { const spinner = ora(`Creating project...`).start(); try { diff --git a/src/services/project.services.ts b/src/services/project.services.ts index fe77e74..030dc17 100644 --- a/src/services/project.services.ts +++ b/src/services/project.services.ts @@ -23,6 +23,17 @@ export const checkForExistingProject = async (): Promise<{initProject: boolean}> return {initProject}; }; +const generateProject = async ({destination, ...rest}: GeneratorInput) => { + const input = { + where: ['.', ''].includes(destination) ? null : destination, + ...rest + }; + + await generate(input); + + await dependencies(input); +}; + export const initNewProject = async (args: string[]): Promise => { const userInputs = initArgs(args); @@ -43,9 +54,7 @@ export const initNewProject = async (args: string[]): Promise => localDevelopment }; - await generate(input); - - await dependencies(); + await generateProject(input); return input; }; diff --git a/src/types/generator.ts b/src/types/generator.ts index bdf4dcb..7975a04 100644 --- a/src/types/generator.ts +++ b/src/types/generator.ts @@ -8,3 +8,7 @@ export interface GeneratorInput { } export type ProjectKind = 'website' | 'app'; + +export type PopulateInput = { + where: string | null; +} & Omit; From 85b40fca12dc77dceb6a630ed008f648159c8b20 Mon Sep 17 00:00:00 2001 From: David Dal Busco Date: Thu, 11 Apr 2024 13:33:37 +0200 Subject: [PATCH 12/13] chore: debug windows --- src/services/deps.services.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/services/deps.services.ts b/src/services/deps.services.ts index 0919cf6..472ed6b 100644 --- a/src/services/deps.services.ts +++ b/src/services/deps.services.ts @@ -22,6 +22,8 @@ export const dependencies = async ({where}: PopulateInput) => { ...(nonNullish(where) && where !== '' ? ['--prefix', where ?? ''] : []) ]; + console.log(pm, args); + await spawn({ command: pm, args, From 7c012ed3afe44103b5e99a68330100cce4baff0a Mon Sep 17 00:00:00 2001 From: David Dal Busco Date: Thu, 11 Apr 2024 13:40:05 +0200 Subject: [PATCH 13/13] feat: cwd --- src/services/deps.services.ts | 12 +++--------- src/utils/cmd.utils.ts | 10 ++++++++-- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/services/deps.services.ts b/src/services/deps.services.ts index 472ed6b..fbd0443 100644 --- a/src/services/deps.services.ts +++ b/src/services/deps.services.ts @@ -17,17 +17,11 @@ export const dependencies = async ({where}: PopulateInput) => { try { const pm = whichPMRuns(); - const args = [ - 'install', - ...(nonNullish(where) && where !== '' ? ['--prefix', where ?? ''] : []) - ]; - - console.log(pm, args); - await spawn({ command: pm, - args, - silentOut: true + args: ['install'], + silentOut: true, + ...(nonNullish(where) && where !== '' && {cwd: where}) }); } finally { spinner.stop(); diff --git a/src/utils/cmd.utils.ts b/src/utils/cmd.utils.ts index 4b58b95..2919431 100644 --- a/src/utils/cmd.utils.ts +++ b/src/utils/cmd.utils.ts @@ -1,3 +1,4 @@ +import {nonNullish} from '@junobuild/utils'; import { spawn as spawnCommand, type ChildProcess, @@ -11,16 +12,21 @@ export const spawn = async ({ args, stdout, silentOut = false, - silentErrors = false + silentErrors = false, + cwd }: { command: string; + cwd?: string; args?: readonly string[]; stdout?: (output: string) => void; silentOut?: boolean; silentErrors?: boolean; }): Promise => { return await new Promise((resolve, reject) => { - const process: ChildProcessWithoutNullStreams = spawnCommand(command, args, { shell: true }); + const process: ChildProcessWithoutNullStreams = spawnCommand(command, args, { + shell: true, + ...(nonNullish(cwd) && {cwd}) + }); process.stdout.on('data', (data) => { if (stdout !== null && stdout !== undefined) {