From 2aec2cdc21f48f9b4f1dd82e2fd16fa3d653ccc5 Mon Sep 17 00:00:00 2001 From: Nate Moore Date: Thu, 7 Mar 2024 12:14:48 -0600 Subject: [PATCH] Adds `create-astro` fallback values for package versions (#10255) * fix(create-astro): add fallback when registry fails to return the current package version * feat(create-astro): inline most current package versions as fallback * test(create-astro): update typescript tests to check for undefined * test(create-astro): properly reset fixtures * refactor: read dependencies from workspace root * refactor: error on missing values --- .changeset/empty-bees-help.md | 5 ++ packages/create-astro/src/actions/context.ts | 2 +- .../create-astro/src/actions/typescript.ts | 4 +- packages/create-astro/src/messages.ts | 7 ++- .../test/fixtures/not-empty/package.json | 2 +- .../test/fixtures/not-empty/tsconfig.json | 4 +- packages/create-astro/test/typescript.test.js | 10 +++- packages/create-astro/test/utils.js | 1 + scripts/cmd/build.js | 51 +++++++++++++++++-- 9 files changed, 69 insertions(+), 17 deletions(-) create mode 100644 .changeset/empty-bees-help.md diff --git a/.changeset/empty-bees-help.md b/.changeset/empty-bees-help.md new file mode 100644 index 0000000000..bb530f85ea --- /dev/null +++ b/.changeset/empty-bees-help.md @@ -0,0 +1,5 @@ +--- +"create-astro": patch +--- + +Fixes an issue where TypeScript and `@astrojs/check` versions would occassionally print as `undefined`. diff --git a/packages/create-astro/src/actions/context.ts b/packages/create-astro/src/actions/context.ts index d052cf0446..4bb76fe719 100644 --- a/packages/create-astro/src/actions/context.ts +++ b/packages/create-astro/src/actions/context.ts @@ -93,7 +93,7 @@ export async function getContext(argv: string[]): Promise { prompt, packageManager, username: getName(), - version: getVersion(packageManager, 'astro'), + version: getVersion(packageManager, 'astro', process.env.ASTRO_VERSION), skipHouston, fancy, dryRun, diff --git a/packages/create-astro/src/actions/typescript.ts b/packages/create-astro/src/actions/typescript.ts index 5e9defe021..9013d338bc 100644 --- a/packages/create-astro/src/actions/typescript.ts +++ b/packages/create-astro/src/actions/typescript.ts @@ -101,8 +101,8 @@ const FILES_TO_UPDATE = { } const [astroCheckVersion, typescriptVersion] = await Promise.all([ - getVersion(options.ctx.packageManager, '@astrojs/check'), - getVersion(options.ctx.packageManager, 'typescript'), + getVersion(options.ctx.packageManager, '@astrojs/check', process.env.ASTRO_CHECK_VERSION), + getVersion(options.ctx.packageManager, 'typescript', process.env.TYPESCRIPT_VERSION), ]); parsedPackageJson.dependencies ??= {}; parsedPackageJson.dependencies['@astrojs/check'] = `^${astroCheckVersion}`; diff --git a/packages/create-astro/src/messages.ts b/packages/create-astro/src/messages.ts index 984ec5affd..966c065f45 100644 --- a/packages/create-astro/src/messages.ts +++ b/packages/create-astro/src/messages.ts @@ -65,10 +65,9 @@ export const getVersion = (packageManager: string, packageName: string, fallback let registry = await getRegistry(packageManager); const { version } = await fetch(`${registry}/${packageName}/latest`, { redirect: 'follow', - }).then( - (res) => res.json(), - () => ({ version: fallback }) - ); + }) + .then((res) => res.json()) + .catch(() => ({ version: fallback })) return resolve(version); }); diff --git a/packages/create-astro/test/fixtures/not-empty/package.json b/packages/create-astro/test/fixtures/not-empty/package.json index 20127bbd33..516149e6d0 100644 --- a/packages/create-astro/test/fixtures/not-empty/package.json +++ b/packages/create-astro/test/fixtures/not-empty/package.json @@ -3,7 +3,7 @@ "private": true, "scripts": { "dev": "astro dev", - "build": "astro check && astro build", + "build": "astro build", "preview": "astro preview" } } diff --git a/packages/create-astro/test/fixtures/not-empty/tsconfig.json b/packages/create-astro/test/fixtures/not-empty/tsconfig.json index 3fd7ae6e83..9e26dfeeb6 100644 --- a/packages/create-astro/test/fixtures/not-empty/tsconfig.json +++ b/packages/create-astro/test/fixtures/not-empty/tsconfig.json @@ -1,3 +1 @@ -{ - "extends": "astro/tsconfigs/strictest" -} \ No newline at end of file +{} \ No newline at end of file diff --git a/packages/create-astro/test/typescript.test.js b/packages/create-astro/test/typescript.test.js index 3d042c7c44..b44f6a864e 100644 --- a/packages/create-astro/test/typescript.test.js +++ b/packages/create-astro/test/typescript.test.js @@ -1,6 +1,6 @@ import assert from 'node:assert/strict'; import fs from 'node:fs'; -import { beforeEach, describe, it } from 'node:test'; +import { after, beforeEach, describe, it } from 'node:test'; import { fileURLToPath } from 'node:url'; import { setupTypeScript, typescript } from '../dist/index.js'; @@ -81,6 +81,7 @@ describe('typescript', async () => { describe('typescript: setup tsconfig', async () => { beforeEach(() => resetFixtures()); + after(() => resetFixtures()); it('none', async () => { const root = new URL('./fixtures/empty/', import.meta.url); @@ -104,6 +105,7 @@ describe('typescript: setup tsconfig', async () => { describe('typescript: setup package', async () => { beforeEach(() => resetFixtures()); + after(() => resetFixtures()); it('none', async () => { const root = new URL('./fixtures/empty/', import.meta.url); @@ -122,7 +124,7 @@ describe('typescript: setup package', async () => { ); await setupTypeScript('strictest', { cwd: fileURLToPath(root), install: false }); - const { scripts } = JSON.parse(fs.readFileSync(packageJson, { encoding: 'utf-8' })); + const { scripts, dependencies } = JSON.parse(fs.readFileSync(packageJson, { encoding: 'utf-8' })); assert.deepEqual( Object.keys(scripts), @@ -130,6 +132,10 @@ describe('typescript: setup package', async () => { 'does not override existing scripts' ); + for (const value of Object.values(dependencies)) { + assert.doesNotMatch(value, /undefined$/, 'does not include undefined values') + } + assert.equal(scripts.build, 'astro check && astro build', 'prepends astro check command'); }); }); diff --git a/packages/create-astro/test/utils.js b/packages/create-astro/test/utils.js index cd5c923caf..62775a2ef9 100644 --- a/packages/create-astro/test/utils.js +++ b/packages/create-astro/test/utils.js @@ -48,6 +48,7 @@ const resetNotEmptyFixture = async () => { build: 'astro build', preview: 'astro preview', }, + dependencies: undefined }); return Promise.all([ diff --git a/scripts/cmd/build.js b/scripts/cmd/build.js index 5ebb666738..94645c1760 100644 --- a/scripts/cmd/build.js +++ b/scripts/cmd/build.js @@ -55,11 +55,13 @@ export default async function build(...args) { const { type = 'module', - version, dependencies = {}, - } = await fs.readFile('./package.json').then((res) => JSON.parse(res.toString())); - // expose PACKAGE_VERSION on process.env for CLI utils - config.define = { 'process.env.PACKAGE_VERSION': JSON.stringify(version) }; + } = await readPackageJSON('./package.json'); + + config.define = {}; + for (const [key, value] of await getDefinedEntries()) { + config.define[`process.env.${key}`] = JSON.stringify(value); + } const format = type === 'module' && !forceCJS ? 'esm' : 'cjs'; const outdir = 'dist'; @@ -138,3 +140,44 @@ async function clean(outdir) { onlyFiles: true, }); } + +/** + * Contextual `define` values to statically replace in the built JS output. + * Available to all packages, but mostly useful for CLIs like `create-astro`. + */ +async function getDefinedEntries() { + const define = { + /** The current version (at the time of building) for the current package, such as `astro` or `@astrojs/sitemap` */ + PACKAGE_VERSION: await getInternalPackageVersion('./package.json'), + /** The current version (at the time of building) for `astro` */ + ASTRO_VERSION: await getInternalPackageVersion(new URL('../../packages/astro/package.json', import.meta.url)), + /** The current version (at the time of building) for `@astrojs/check` */ + ASTRO_CHECK_VERSION: await getWorkspacePackageVersion('@astrojs/check'), + /** The current version (at the time of building) for `typescript` */ + TYPESCRIPT_VERSION: await getWorkspacePackageVersion('typescript'), + } + for (const [key, value] of Object.entries(define)) { + if (value === undefined) { + delete define[key]; + } + } + return Object.entries(define); +} + +async function readPackageJSON(path) { + return await fs.readFile(path, { encoding: 'utf8' }).then((res) => JSON.parse(res)); +} + +async function getInternalPackageVersion(path) { + return readPackageJSON(path).then(res => res.version); +} + +async function getWorkspacePackageVersion(packageName) { + const { dependencies, devDependencies } = await readPackageJSON(new URL('../../package.json', import.meta.url)); + const deps = { ...dependencies, ...devDependencies }; + const version = deps[packageName]; + if (!version) { + throw new Error(`Unable to resolve "${packageName}". Is it a depdendency of the workspace root?`) + } + return version.replace(/^\D+/, ''); +}