From 749aa11fb0f3c083926fb86e4d3e16a1dd5ba3f9 Mon Sep 17 00:00:00 2001 From: bholmesdev Date: Thu, 7 Jul 2022 17:20:41 -0400 Subject: [PATCH 01/13] feat: support adapters and third part integrations by keywords --- packages/astro/src/core/add/index.ts | 171 +++++++++++++++++++++------ 1 file changed, 136 insertions(+), 35 deletions(-) diff --git a/packages/astro/src/core/add/index.ts b/packages/astro/src/core/add/index.ts index 5a1fb9cf6c2a..de84e035a332 100644 --- a/packages/astro/src/core/add/index.ts +++ b/packages/astro/src/core/add/index.ts @@ -3,7 +3,7 @@ import boxen from 'boxen'; import { diffWords } from 'diff'; import { execa } from 'execa'; import { existsSync, promises as fs } from 'fs'; -import { bold, cyan, dim, green, magenta } from 'kleur/colors'; +import { bold, cyan, dim, green, magenta, yellow } from 'kleur/colors'; import ora from 'ora'; import path from 'path'; import preferredPM from 'preferred-pm'; @@ -32,6 +32,7 @@ export interface IntegrationInfo { id: string; packageName: string; dependencies: [name: string, version: string][]; + type: 'integration' | 'adapter'; } const ALIASES = new Map([ ['solid', 'solid-js'], @@ -120,7 +121,11 @@ export default async function add(names: string[], { cwd, flags, logging, teleme debug('add', 'Astro config ensured `defineConfig`'); for (const integration of integrations) { - await addIntegration(ast, integration); + if (integration.type === 'adapter') { + await setAdapter(ast, integration); + } else { + await addIntegration(ast, integration); + } debug('add', `Astro config added integration ${integration.id}`); } } catch (err) { @@ -314,6 +319,50 @@ async function addIntegration(ast: t.File, integration: IntegrationInfo) { }); } +async function setAdapter(ast: t.File, adapter: IntegrationInfo) { + const adapterId = t.identifier(toIdent(adapter.id)); + + ensureImport( + ast, + t.importDeclaration( + [t.importDefaultSpecifier(adapterId)], + t.stringLiteral(adapter.packageName) + ) + ); + + visit(ast, { + // eslint-disable-next-line @typescript-eslint/no-shadow + ExportDefaultDeclaration(path) { + if (!t.isCallExpression(path.node.declaration)) return; + + const configObject = path.node.declaration.arguments[0]; + if (!t.isObjectExpression(configObject)) return; + + let adapterProp = configObject.properties.find((prop) => { + if (prop.type !== 'ObjectProperty') return false; + if (prop.key.type === 'Identifier') { + if (prop.key.name === 'adapter') return true; + } + if (prop.key.type === 'StringLiteral') { + if (prop.key.value === 'adapter') return true; + } + return false; + }) as t.ObjectProperty | undefined; + + const adapterCall = t.callExpression(adapterId, []); + + if (!adapterProp) { + configObject.properties.push( + t.objectProperty(t.identifier('adapter'), adapterCall) + ); + return; + } + + adapterProp.value = adapterCall; + }, + }); +} + const enum UpdateResult { none, updated, @@ -479,46 +528,98 @@ async function tryToInstallIntegrations({ } } -export async function validateIntegrations(integrations: string[]): Promise { - const spinner = ora('Resolving integrations...').start(); - const integrationEntries = await Promise.all( - integrations.map(async (integration): Promise => { - const parsed = parseIntegrationName(integration); - if (!parsed) { - spinner.fail(); - throw new Error(`${integration} does not appear to be a valid package name!`); - } +async function fetchPackageJson(scope: string | undefined, name: string, tag: string): Promise { + const packageName = `${scope ? `@${scope}/` : ''}${name}`; + const res = await fetch(`https://registry.npmjs.org/${packageName}/${tag}`) + if (res.status === 404) { + return new Error(); + } else { + return await res.json(); + } +} - let { scope = '', name, tag } = parsed; - // Allow third-party integrations starting with `astro-` namespace - if (!name.startsWith('astro-')) { - scope = `astrojs`; - } - const packageName = `${scope ? `@${scope}/` : ''}${name}`; +export async function validateIntegrations(integrations: string[]): Promise { + const spinner = ora('Resolving packages...').start(); + try { + const integrationEntries = await Promise.all( + integrations.map(async (integration): Promise => { + const parsed = parseIntegrationName(integration); + if (!parsed) { + throw new Error(`${integration} does not appear to be a valid package name!`); + } - const result = await fetch(`https://registry.npmjs.org/${packageName}/${tag}`).then((res) => { - if (res.status === 404) { - spinner.fail(); - throw new Error(`Unable to fetch ${packageName}. Does this package exist?`); + let { scope, name, tag } = parsed; + let pkgJson = null; + let pkgType: 'first-party' | 'third-party' = 'first-party'; + + if (!scope) { + const firstPartyPkgCheck = await fetchPackageJson('astrojs', name, tag); + if (firstPartyPkgCheck instanceof Error) { + spinner.warn(yellow(`${bold(integration)} is not an official Astro package. Use at your own risk!`)); + const response = await prompts({ + type: 'confirm', + name: 'askToContinue', + message: 'Continue?', + initial: true, + }); + if (!response.askToContinue) { + throw new Error('No problem! Find our official integrations at https://astro.build/integrations'); + } + spinner.start('Resolving with third party packages...'); + pkgType = 'third-party'; + } else { + pkgJson = firstPartyPkgCheck as any; + } } - return res.json(); - }); + if (pkgType === 'third-party') { + const thirdPartyPkgCheck = await fetchPackageJson(scope, name, tag); + if (thirdPartyPkgCheck instanceof Error) { + throw new Error( + `Unable to fetch ${bold(integration)}. Does the package exist?`, + ); + } else { + pkgJson = thirdPartyPkgCheck as any; + } + } + + const resolvedScope = pkgType === 'first-party' ? 'astrojs' : scope; + const packageName = `${resolvedScope ? `@${resolvedScope}/` : ''}${name}`; - let dependencies: IntegrationInfo['dependencies'] = [ - [result['name'], `^${result['version']}`], - ]; + let dependencies: IntegrationInfo['dependencies'] = [ + [pkgJson['name'], `^${pkgJson['version']}`], + ]; - if (result['peerDependencies']) { - for (const peer in result['peerDependencies']) { - dependencies.push([peer, result['peerDependencies'][peer]]); + if (pkgJson['peerDependencies']) { + for (const peer in pkgJson['peerDependencies']) { + dependencies.push([peer, pkgJson['peerDependencies'][peer]]); + } } - } - return { id: integration, packageName, dependencies }; - }) - ); - spinner.succeed(); - return integrationEntries; + let integrationType: IntegrationInfo['type']; + const keywords = Array.isArray(pkgJson['keywords']) ? pkgJson['keywords'] : []; + if (keywords.includes('astro-integration')) { + integrationType = 'integration'; + } else if (keywords.includes('astro-adapter')) { + integrationType = 'adapter'; + } else { + throw new Error( + `${bold(packageName)} doesn't appear to be an integration or an adapter. Find our official integrations at https://astro.build/integrations` + ); + } + + return { id: integration, packageName, dependencies, type: integrationType }; + }) + ); + spinner.succeed(); + return integrationEntries; + } catch (e) { + if (e instanceof Error) { + spinner.fail(e.message); + process.exit(0); + } else { + throw e; + } + } } function parseIntegrationName(spec: string) { From 7be8bdfed9f6302052b7d7282087b33427bf5ca2 Mon Sep 17 00:00:00 2001 From: bholmesdev Date: Thu, 7 Jul 2022 17:21:27 -0400 Subject: [PATCH 02/13] refactor: add keywords to all official integrations --- packages/integrations/cloudflare/package.json | 1 + packages/integrations/deno/package.json | 1 + packages/integrations/image/package.json | 1 + packages/integrations/lit/package.json | 1 + packages/integrations/mdx/package.json | 1 + packages/integrations/netlify/package.json | 1 + packages/integrations/node/package.json | 1 + packages/integrations/partytown/package.json | 1 + packages/integrations/preact/package.json | 1 + packages/integrations/prefetch/package.json | 1 + packages/integrations/react/package.json | 1 + packages/integrations/sitemap/package.json | 1 + packages/integrations/solid/package.json | 1 + packages/integrations/svelte/package.json | 1 + packages/integrations/tailwind/package.json | 1 + packages/integrations/turbolinks/package.json | 1 + packages/integrations/vercel/package.json | 1 + packages/integrations/vue/package.json | 1 + 18 files changed, 18 insertions(+) diff --git a/packages/integrations/cloudflare/package.json b/packages/integrations/cloudflare/package.json index 54856276acd6..cd4a637b7bf7 100644 --- a/packages/integrations/cloudflare/package.json +++ b/packages/integrations/cloudflare/package.json @@ -11,6 +11,7 @@ "url": "https://github.com/withastro/astro.git", "directory": "packages/integrations/cloudflare" }, + "keywords": ["astro-adapter"], "bugs": "https://github.com/withastro/astro/issues", "homepage": "https://astro.build", "exports": { diff --git a/packages/integrations/deno/package.json b/packages/integrations/deno/package.json index b5d6f06ea381..02ed83554578 100644 --- a/packages/integrations/deno/package.json +++ b/packages/integrations/deno/package.json @@ -11,6 +11,7 @@ "url": "https://github.com/withastro/astro.git", "directory": "packages/integrations/deno" }, + "keywords": ["astro-adapter"], "bugs": "https://github.com/withastro/astro/issues", "homepage": "https://astro.build", "exports": { diff --git a/packages/integrations/image/package.json b/packages/integrations/image/package.json index 538601a4989d..d8fd2b5f3737 100644 --- a/packages/integrations/image/package.json +++ b/packages/integrations/image/package.json @@ -12,6 +12,7 @@ "directory": "packages/integrations/image" }, "keywords": [ + "astro-integration", "astro-component", "withastro", "image" diff --git a/packages/integrations/lit/package.json b/packages/integrations/lit/package.json index 12beab2451e7..bfda1e1af72e 100644 --- a/packages/integrations/lit/package.json +++ b/packages/integrations/lit/package.json @@ -12,6 +12,7 @@ "directory": "packages/integrations/lit" }, "keywords": [ + "astro-integration", "astro-component", "renderer", "lit" diff --git a/packages/integrations/mdx/package.json b/packages/integrations/mdx/package.json index 285690337852..1f76f471831c 100644 --- a/packages/integrations/mdx/package.json +++ b/packages/integrations/mdx/package.json @@ -12,6 +12,7 @@ "directory": "packages/integrations/mdx" }, "keywords": [ + "astro-integration", "astro-component", "renderer", "mdx" diff --git a/packages/integrations/netlify/package.json b/packages/integrations/netlify/package.json index 2e2c15432ff4..5eee7a595b6a 100644 --- a/packages/integrations/netlify/package.json +++ b/packages/integrations/netlify/package.json @@ -11,6 +11,7 @@ "url": "https://github.com/withastro/astro.git", "directory": "packages/integrations/netlify" }, + "keywords": ["astro-adapter"], "bugs": "https://github.com/withastro/astro/issues", "homepage": "https://astro.build", "exports": { diff --git a/packages/integrations/node/package.json b/packages/integrations/node/package.json index e4a37cee3c21..de42e7d16d68 100644 --- a/packages/integrations/node/package.json +++ b/packages/integrations/node/package.json @@ -11,6 +11,7 @@ "url": "https://github.com/withastro/astro.git", "directory": "packages/integrations/node" }, + "keywords": ["astro-adapter"], "bugs": "https://github.com/withastro/astro/issues", "homepage": "https://astro.build", "exports": { diff --git a/packages/integrations/partytown/package.json b/packages/integrations/partytown/package.json index 2833e3d611a1..e775e30cad19 100644 --- a/packages/integrations/partytown/package.json +++ b/packages/integrations/partytown/package.json @@ -12,6 +12,7 @@ "directory": "packages/integrations/partytown" }, "keywords": [ + "astro-integration", "astro-component", "analytics", "performance" diff --git a/packages/integrations/preact/package.json b/packages/integrations/preact/package.json index 426a48444409..36b04827b0bb 100644 --- a/packages/integrations/preact/package.json +++ b/packages/integrations/preact/package.json @@ -12,6 +12,7 @@ "directory": "packages/integrations/preact" }, "keywords": [ + "astro-integration", "astro-component", "renderer", "preact" diff --git a/packages/integrations/prefetch/package.json b/packages/integrations/prefetch/package.json index 3b07034ebe87..4ce1db137c0c 100644 --- a/packages/integrations/prefetch/package.json +++ b/packages/integrations/prefetch/package.json @@ -11,6 +11,7 @@ "url": "https://github.com/withastro/astro.git", "directory": "packages/astro-prefetch" }, + "keywords": ["astro-integration"], "bugs": "https://github.com/withastro/astro/issues", "homepage": "https://astro.build", "exports": { diff --git a/packages/integrations/react/package.json b/packages/integrations/react/package.json index 0bc3937bfb46..dae26dcd699a 100644 --- a/packages/integrations/react/package.json +++ b/packages/integrations/react/package.json @@ -12,6 +12,7 @@ "directory": "packages/integrations/react" }, "keywords": [ + "astro-integration", "astro-component", "renderer", "react" diff --git a/packages/integrations/sitemap/package.json b/packages/integrations/sitemap/package.json index ac98fa05c460..8aa96f33dc27 100644 --- a/packages/integrations/sitemap/package.json +++ b/packages/integrations/sitemap/package.json @@ -12,6 +12,7 @@ "directory": "packages/integrations/sitemap" }, "keywords": [ + "astro-integration", "astro-component", "seo", "sitemap" diff --git a/packages/integrations/solid/package.json b/packages/integrations/solid/package.json index c6fe8c40abaf..42f68dc9379c 100644 --- a/packages/integrations/solid/package.json +++ b/packages/integrations/solid/package.json @@ -12,6 +12,7 @@ "directory": "packages/integrations/solid" }, "keywords": [ + "astro-integration", "astro-component", "renderer", "solid" diff --git a/packages/integrations/svelte/package.json b/packages/integrations/svelte/package.json index 53ce95147aa4..755fbc8d3807 100644 --- a/packages/integrations/svelte/package.json +++ b/packages/integrations/svelte/package.json @@ -12,6 +12,7 @@ "directory": "packages/integrations/svelte" }, "keywords": [ + "astro-integration", "astro-component", "renderer", "svelte" diff --git a/packages/integrations/tailwind/package.json b/packages/integrations/tailwind/package.json index 2c200b77204f..86b58d74c181 100644 --- a/packages/integrations/tailwind/package.json +++ b/packages/integrations/tailwind/package.json @@ -12,6 +12,7 @@ "directory": "packages/integrations/tailwind" }, "keywords": [ + "astro-integration", "astro-component" ], "bugs": "https://github.com/withastro/astro/issues", diff --git a/packages/integrations/turbolinks/package.json b/packages/integrations/turbolinks/package.json index c3b54f1e8df4..5b7310fcafab 100644 --- a/packages/integrations/turbolinks/package.json +++ b/packages/integrations/turbolinks/package.json @@ -12,6 +12,7 @@ "directory": "packages/integrations/turbolinks" }, "keywords": [ + "astro-integration", "astro-component", "performance" ], diff --git a/packages/integrations/vercel/package.json b/packages/integrations/vercel/package.json index 3f09baf01461..055f08b8d4e7 100644 --- a/packages/integrations/vercel/package.json +++ b/packages/integrations/vercel/package.json @@ -10,6 +10,7 @@ "url": "https://github.com/withastro/astro.git", "directory": "packages/integrations/vercel" }, + "keywords": ["astro-adapter"], "bugs": "https://github.com/withastro/astro/issues", "homepage": "https://astro.build", "exports": { diff --git a/packages/integrations/vue/package.json b/packages/integrations/vue/package.json index a6b2989fe9b4..03d7b7b2fe1e 100644 --- a/packages/integrations/vue/package.json +++ b/packages/integrations/vue/package.json @@ -12,6 +12,7 @@ "directory": "packages/integrations/vue" }, "keywords": [ + "astro-integration", "astro-component", "renderer", "vue" From 250b11d6767e7bfd4bc96b9bfadf69440b5fbdf3 Mon Sep 17 00:00:00 2001 From: bholmesdev Date: Thu, 7 Jul 2022 17:31:00 -0400 Subject: [PATCH 03/13] docs: add adapter ex to astro add help --- packages/astro/src/core/add/index.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/astro/src/core/add/index.ts b/packages/astro/src/core/add/index.ts index de84e035a332..5d9303f454d6 100644 --- a/packages/astro/src/core/add/index.ts +++ b/packages/astro/src/core/add/index.ts @@ -52,7 +52,7 @@ export default async function add(names: string[], { cwd, flags, logging, teleme if (flags.help || names.length === 0) { printHelp({ commandName: 'astro add', - usage: '[...integrations]', + usage: '[...integrations/adapters]', tables: { Flags: [ ['--yes', 'Accept all prompts.'], @@ -71,6 +71,11 @@ export default async function add(names: string[], { cwd, flags, logging, teleme ['partytown', 'astro add partytown'], ['sitemap', 'astro add sitemap'], ], + 'Example: Add an Adapter': [ + ['netlify', 'astro add netlify'], + ['vercel', 'astro add vercel'], + ['deno', 'astro add deno'], + ], }, description: `Check out the full integration catalog: ${cyan( 'https://astro.build/integrations' From f54547aa44e69bb39744e19e4997074867138bc6 Mon Sep 17 00:00:00 2001 From: bholmesdev Date: Thu, 7 Jul 2022 17:32:58 -0400 Subject: [PATCH 04/13] nit: clarify astro add usage --- packages/astro/src/core/add/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/astro/src/core/add/index.ts b/packages/astro/src/core/add/index.ts index 5d9303f454d6..e7631f30ab04 100644 --- a/packages/astro/src/core/add/index.ts +++ b/packages/astro/src/core/add/index.ts @@ -52,7 +52,7 @@ export default async function add(names: string[], { cwd, flags, logging, teleme if (flags.help || names.length === 0) { printHelp({ commandName: 'astro add', - usage: '[...integrations/adapters]', + usage: '[...integrations] [...adapters]', tables: { Flags: [ ['--yes', 'Accept all prompts.'], From b0e73e7b580884d5cdd6f91bb6b39a4866612abf Mon Sep 17 00:00:00 2001 From: bholmesdev Date: Thu, 7 Jul 2022 17:34:15 -0400 Subject: [PATCH 05/13] nit: highlight link --- packages/astro/src/core/add/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/astro/src/core/add/index.ts b/packages/astro/src/core/add/index.ts index e7631f30ab04..bcb3a208da9e 100644 --- a/packages/astro/src/core/add/index.ts +++ b/packages/astro/src/core/add/index.ts @@ -568,7 +568,7 @@ export async function validateIntegrations(integrations: string[]): Promise Date: Thu, 7 Jul 2022 17:53:20 -0400 Subject: [PATCH 06/13] fix: use process.exit(1) on error --- packages/astro/src/core/add/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/astro/src/core/add/index.ts b/packages/astro/src/core/add/index.ts index bcb3a208da9e..c7cf481e5f7b 100644 --- a/packages/astro/src/core/add/index.ts +++ b/packages/astro/src/core/add/index.ts @@ -620,7 +620,7 @@ export async function validateIntegrations(integrations: string[]): Promise Date: Thu, 7 Jul 2022 17:54:53 -0400 Subject: [PATCH 07/13] chore: changeset --- .changeset/lucky-bottles-wait.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 .changeset/lucky-bottles-wait.md diff --git a/.changeset/lucky-bottles-wait.md b/.changeset/lucky-bottles-wait.md new file mode 100644 index 000000000000..271c28ed4776 --- /dev/null +++ b/.changeset/lucky-bottles-wait.md @@ -0,0 +1,23 @@ +--- +'astro': patch +'@astrojs/cloudflare': patch +'@astrojs/deno': patch +'@astrojs/image': patch +'@astrojs/lit': patch +'@astrojs/mdx': patch +'@astrojs/netlify': patch +'@astrojs/node': patch +'@astrojs/partytown': patch +'@astrojs/preact': patch +'@astrojs/prefetch': patch +'@astrojs/react': patch +'@astrojs/sitemap': patch +'@astrojs/solid-js': patch +'@astrojs/svelte': patch +'@astrojs/tailwind': patch +'@astrojs/turbolinks': patch +'@astrojs/vercel': patch +'@astrojs/vue': patch +--- + +[astro add] Support adapters and third party packages From 54808020d108f8ad2e8394b0619127201facb1fe Mon Sep 17 00:00:00 2001 From: bholmesdev Date: Fri, 8 Jul 2022 10:44:28 -0400 Subject: [PATCH 08/13] nit: bold integration name --- packages/astro/src/core/add/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/astro/src/core/add/index.ts b/packages/astro/src/core/add/index.ts index c7cf481e5f7b..db3d18fc767e 100644 --- a/packages/astro/src/core/add/index.ts +++ b/packages/astro/src/core/add/index.ts @@ -550,7 +550,7 @@ export async function validateIntegrations(integrations: string[]): Promise => { const parsed = parseIntegrationName(integration); if (!parsed) { - throw new Error(`${integration} does not appear to be a valid package name!`); + throw new Error(`${bold(integration)} does not appear to be a valid package name!`); } let { scope, name, tag } = parsed; From 1a459f152bc7b7991db289999f7393e5be64ea3e Mon Sep 17 00:00:00 2001 From: bholmesdev Date: Fri, 8 Jul 2022 11:28:15 -0400 Subject: [PATCH 09/13] fix: log install instructions for adapters instead --- packages/astro/src/core/add/index.ts | 96 ++++++++++++---------------- 1 file changed, 40 insertions(+), 56 deletions(-) diff --git a/packages/astro/src/core/add/index.ts b/packages/astro/src/core/add/index.ts index db3d18fc767e..5b5478c31c7c 100644 --- a/packages/astro/src/core/add/index.ts +++ b/packages/astro/src/core/add/index.ts @@ -105,8 +105,12 @@ export default async function add(names: string[], { cwd, flags, logging, teleme // Some packages might have a common alias! We normalize those here. const integrationNames = names.map((name) => (ALIASES.has(name) ? ALIASES.get(name)! : name)); - const integrations = await validateIntegrations(integrationNames); + const integrationsAndAdapters = await validateIntegrations(integrationNames); + const integrations = integrationsAndAdapters.filter(isIntegration); + const adapters = integrationsAndAdapters.filter(isAdapter); + logAdapterInstallInstructions(adapters, logging); + if (integrations.length) { let ast: t.File | null = null; try { ast = await parseAstroConfig(configURL); @@ -126,12 +130,8 @@ export default async function add(names: string[], { cwd, flags, logging, teleme debug('add', 'Astro config ensured `defineConfig`'); for (const integration of integrations) { - if (integration.type === 'adapter') { - await setAdapter(ast, integration); - } else { await addIntegration(ast, integration); - } - debug('add', `Astro config added integration ${integration.id}`); + debug('add', `Astro config added ${integration.type} ${integration.id}`); } } catch (err) { debug('add', 'Error parsing/modifying astro config: ', err); @@ -139,7 +139,6 @@ export default async function add(names: string[], { cwd, flags, logging, teleme } let configResult: UpdateResult | undefined; - let installResult: UpdateResult | undefined; if (ast) { try { @@ -165,7 +164,7 @@ export default async function add(names: string[], { cwd, flags, logging, teleme const missingDeps = integrations.filter( (integration) => !deps.includes(integration.packageName) ); - if (missingDeps.length === 0) { + if (missingDeps.length === 0 && !adapters.length) { info(logging, null, msg.success(`Configuration up-to-date.`)); return; } @@ -175,13 +174,19 @@ export default async function add(names: string[], { cwd, flags, logging, teleme break; } } + } - installResult = await tryToInstallIntegrations({ integrations, cwd, flags, logging }); + let installResult = await tryToInstallIntegrations({ + integrations: integrationsAndAdapters, + cwd, + flags, + logging + }); switch (installResult) { case UpdateResult.updated: { - const len = integrations.length; - if (integrations.find((integration) => integration.id === 'tailwind')) { + const len = integrationsAndAdapters.length; + if (integrationsAndAdapters.find((entry) => entry.id === 'tailwind')) { const possibleConfigFiles = [ './tailwind.config.cjs', './tailwind.config.mjs', @@ -214,7 +219,7 @@ export default async function add(names: string[], { cwd, flags, logging, teleme debug('add', `Using existing Tailwind configuration`); } } - const list = integrations.map((integration) => ` - ${integration.packageName}`).join('\n'); + const list = integrationsAndAdapters.map((entry) => ` - ${entry.packageName}`).join('\n'); info( logging, null, @@ -268,6 +273,29 @@ Documentation: https://docs.astro.build/en/guides/integrations-guide/`; return err; } +function isIntegration(integration: IntegrationInfo): integration is IntegrationInfo & { type: 'integration' } { + return integration.type === 'integration'; +} + +function isAdapter(integration: IntegrationInfo): integration is IntegrationInfo & { type: 'adapter' } { + return integration.type === 'adapter'; +} + +function logAdapterInstallInstructions(adapters: (IntegrationInfo & { type: 'adapter' })[], logging: LogOptions) { + for (const adapter of adapters) { + info( + logging, + null, + `\n ${cyan(`Check our deployment docs for ${bold(adapter.packageName)} to update your "adapter" config.`)}` + ); + } + info( + logging, + null, + `\n ${bold(cyan('https://docs.astro.build/en/guides/deploy/'))}` + ); +} + async function addIntegration(ast: t.File, integration: IntegrationInfo) { const integrationId = t.identifier(toIdent(integration.id)); @@ -324,50 +352,6 @@ async function addIntegration(ast: t.File, integration: IntegrationInfo) { }); } -async function setAdapter(ast: t.File, adapter: IntegrationInfo) { - const adapterId = t.identifier(toIdent(adapter.id)); - - ensureImport( - ast, - t.importDeclaration( - [t.importDefaultSpecifier(adapterId)], - t.stringLiteral(adapter.packageName) - ) - ); - - visit(ast, { - // eslint-disable-next-line @typescript-eslint/no-shadow - ExportDefaultDeclaration(path) { - if (!t.isCallExpression(path.node.declaration)) return; - - const configObject = path.node.declaration.arguments[0]; - if (!t.isObjectExpression(configObject)) return; - - let adapterProp = configObject.properties.find((prop) => { - if (prop.type !== 'ObjectProperty') return false; - if (prop.key.type === 'Identifier') { - if (prop.key.name === 'adapter') return true; - } - if (prop.key.type === 'StringLiteral') { - if (prop.key.value === 'adapter') return true; - } - return false; - }) as t.ObjectProperty | undefined; - - const adapterCall = t.callExpression(adapterId, []); - - if (!adapterProp) { - configObject.properties.push( - t.objectProperty(t.identifier('adapter'), adapterCall) - ); - return; - } - - adapterProp.value = adapterCall; - }, - }); -} - const enum UpdateResult { none, updated, From b263315d62d2b45fb0aa4482b32b5b602de99438 Mon Sep 17 00:00:00 2001 From: bholmesdev Date: Fri, 8 Jul 2022 11:29:30 -0400 Subject: [PATCH 10/13] nit: change to logAdapterConfigInstructions --- packages/astro/src/core/add/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/astro/src/core/add/index.ts b/packages/astro/src/core/add/index.ts index 5b5478c31c7c..1a9a9e3b23e3 100644 --- a/packages/astro/src/core/add/index.ts +++ b/packages/astro/src/core/add/index.ts @@ -109,7 +109,7 @@ export default async function add(names: string[], { cwd, flags, logging, teleme const integrations = integrationsAndAdapters.filter(isIntegration); const adapters = integrationsAndAdapters.filter(isAdapter); - logAdapterInstallInstructions(adapters, logging); + logAdapterConfigInstructions(adapters, logging); if (integrations.length) { let ast: t.File | null = null; try { @@ -281,7 +281,7 @@ function isAdapter(integration: IntegrationInfo): integration is IntegrationInfo return integration.type === 'adapter'; } -function logAdapterInstallInstructions(adapters: (IntegrationInfo & { type: 'adapter' })[], logging: LogOptions) { +function logAdapterConfigInstructions(adapters: (IntegrationInfo & { type: 'adapter' })[], logging: LogOptions) { for (const adapter of adapters) { info( logging, From 2d1525f1664f17ab19418938c1f4315dd25c9633 Mon Sep 17 00:00:00 2001 From: bholmesdev Date: Fri, 8 Jul 2022 15:16:08 -0400 Subject: [PATCH 11/13] Revert "fix: log install instructions for adapters instead" This reverts commit 1a459f152bc7b7991db289999f7393e5be64ea3e. --- packages/astro/src/core/add/index.ts | 96 ++++++++++++++++------------ 1 file changed, 56 insertions(+), 40 deletions(-) diff --git a/packages/astro/src/core/add/index.ts b/packages/astro/src/core/add/index.ts index 1a9a9e3b23e3..db3d18fc767e 100644 --- a/packages/astro/src/core/add/index.ts +++ b/packages/astro/src/core/add/index.ts @@ -105,12 +105,8 @@ export default async function add(names: string[], { cwd, flags, logging, teleme // Some packages might have a common alias! We normalize those here. const integrationNames = names.map((name) => (ALIASES.has(name) ? ALIASES.get(name)! : name)); - const integrationsAndAdapters = await validateIntegrations(integrationNames); - const integrations = integrationsAndAdapters.filter(isIntegration); - const adapters = integrationsAndAdapters.filter(isAdapter); + const integrations = await validateIntegrations(integrationNames); - logAdapterConfigInstructions(adapters, logging); - if (integrations.length) { let ast: t.File | null = null; try { ast = await parseAstroConfig(configURL); @@ -130,8 +126,12 @@ export default async function add(names: string[], { cwd, flags, logging, teleme debug('add', 'Astro config ensured `defineConfig`'); for (const integration of integrations) { + if (integration.type === 'adapter') { + await setAdapter(ast, integration); + } else { await addIntegration(ast, integration); - debug('add', `Astro config added ${integration.type} ${integration.id}`); + } + debug('add', `Astro config added integration ${integration.id}`); } } catch (err) { debug('add', 'Error parsing/modifying astro config: ', err); @@ -139,6 +139,7 @@ export default async function add(names: string[], { cwd, flags, logging, teleme } let configResult: UpdateResult | undefined; + let installResult: UpdateResult | undefined; if (ast) { try { @@ -164,7 +165,7 @@ export default async function add(names: string[], { cwd, flags, logging, teleme const missingDeps = integrations.filter( (integration) => !deps.includes(integration.packageName) ); - if (missingDeps.length === 0 && !adapters.length) { + if (missingDeps.length === 0) { info(logging, null, msg.success(`Configuration up-to-date.`)); return; } @@ -174,19 +175,13 @@ export default async function add(names: string[], { cwd, flags, logging, teleme break; } } - } - let installResult = await tryToInstallIntegrations({ - integrations: integrationsAndAdapters, - cwd, - flags, - logging - }); + installResult = await tryToInstallIntegrations({ integrations, cwd, flags, logging }); switch (installResult) { case UpdateResult.updated: { - const len = integrationsAndAdapters.length; - if (integrationsAndAdapters.find((entry) => entry.id === 'tailwind')) { + const len = integrations.length; + if (integrations.find((integration) => integration.id === 'tailwind')) { const possibleConfigFiles = [ './tailwind.config.cjs', './tailwind.config.mjs', @@ -219,7 +214,7 @@ export default async function add(names: string[], { cwd, flags, logging, teleme debug('add', `Using existing Tailwind configuration`); } } - const list = integrationsAndAdapters.map((entry) => ` - ${entry.packageName}`).join('\n'); + const list = integrations.map((integration) => ` - ${integration.packageName}`).join('\n'); info( logging, null, @@ -273,29 +268,6 @@ Documentation: https://docs.astro.build/en/guides/integrations-guide/`; return err; } -function isIntegration(integration: IntegrationInfo): integration is IntegrationInfo & { type: 'integration' } { - return integration.type === 'integration'; -} - -function isAdapter(integration: IntegrationInfo): integration is IntegrationInfo & { type: 'adapter' } { - return integration.type === 'adapter'; -} - -function logAdapterConfigInstructions(adapters: (IntegrationInfo & { type: 'adapter' })[], logging: LogOptions) { - for (const adapter of adapters) { - info( - logging, - null, - `\n ${cyan(`Check our deployment docs for ${bold(adapter.packageName)} to update your "adapter" config.`)}` - ); - } - info( - logging, - null, - `\n ${bold(cyan('https://docs.astro.build/en/guides/deploy/'))}` - ); -} - async function addIntegration(ast: t.File, integration: IntegrationInfo) { const integrationId = t.identifier(toIdent(integration.id)); @@ -352,6 +324,50 @@ async function addIntegration(ast: t.File, integration: IntegrationInfo) { }); } +async function setAdapter(ast: t.File, adapter: IntegrationInfo) { + const adapterId = t.identifier(toIdent(adapter.id)); + + ensureImport( + ast, + t.importDeclaration( + [t.importDefaultSpecifier(adapterId)], + t.stringLiteral(adapter.packageName) + ) + ); + + visit(ast, { + // eslint-disable-next-line @typescript-eslint/no-shadow + ExportDefaultDeclaration(path) { + if (!t.isCallExpression(path.node.declaration)) return; + + const configObject = path.node.declaration.arguments[0]; + if (!t.isObjectExpression(configObject)) return; + + let adapterProp = configObject.properties.find((prop) => { + if (prop.type !== 'ObjectProperty') return false; + if (prop.key.type === 'Identifier') { + if (prop.key.name === 'adapter') return true; + } + if (prop.key.type === 'StringLiteral') { + if (prop.key.value === 'adapter') return true; + } + return false; + }) as t.ObjectProperty | undefined; + + const adapterCall = t.callExpression(adapterId, []); + + if (!adapterProp) { + configObject.properties.push( + t.objectProperty(t.identifier('adapter'), adapterCall) + ); + return; + } + + adapterProp.value = adapterCall; + }, + }); +} + const enum UpdateResult { none, updated, From 4125d77d0c93a196ee6c55591995da421a0f11c2 Mon Sep 17 00:00:00 2001 From: bholmesdev Date: Fri, 8 Jul 2022 16:25:26 -0400 Subject: [PATCH 12/13] feat: add hardcoded adapter export map --- packages/astro/src/core/add/index.ts | 51 +++++++++++++++++++++++++--- 1 file changed, 46 insertions(+), 5 deletions(-) diff --git a/packages/astro/src/core/add/index.ts b/packages/astro/src/core/add/index.ts index db3d18fc767e..25b093b75e40 100644 --- a/packages/astro/src/core/add/index.ts +++ b/packages/astro/src/core/add/index.ts @@ -48,6 +48,14 @@ module.exports = { plugins: [], }\n`; +const OFFICIAL_ADAPTER_TO_IMPORT_MAP: Record = { + 'netlify': '@astrojs/netlify/functions', + 'vercel': '@astrojs/vercel/serverless', + 'cloudflare': '@astrojs/cloudflare', + 'node': '@astrojs/node', + 'deno': '@astrojs/deno', +} + export default async function add(names: string[], { cwd, flags, logging, telemetry }: AddOptions) { if (flags.help || names.length === 0) { printHelp({ @@ -126,8 +134,13 @@ export default async function add(names: string[], { cwd, flags, logging, teleme debug('add', 'Astro config ensured `defineConfig`'); for (const integration of integrations) { - if (integration.type === 'adapter') { - await setAdapter(ast, integration); + if (isAdapter(integration)) { + const officialExportName = OFFICIAL_ADAPTER_TO_IMPORT_MAP[integration.id]; + if (officialExportName) { + await setAdapter(ast, integration, officialExportName); + } else { + logAdapterConfigInstructions(integration, logging); + } } else { await addIntegration(ast, integration); } @@ -143,7 +156,13 @@ export default async function add(names: string[], { cwd, flags, logging, teleme if (ast) { try { - configResult = await updateAstroConfig({ configURL, ast, flags, logging }); + configResult = await updateAstroConfig({ + configURL, + ast, + flags, + logging, + logAdapterInstructions: integrations.some(isAdapter), + }); } catch (err) { debug('add', 'Error updating astro config', err); throw createPrettyError(err as Error); @@ -241,6 +260,18 @@ export default async function add(names: string[], { cwd, flags, logging, teleme } } +function isAdapter(integration: IntegrationInfo): integration is IntegrationInfo & { type: 'adapter' } { + return integration.type === 'adapter'; +} + +function logAdapterConfigInstructions(adapter: (IntegrationInfo & { type: 'adapter' }), logging: LogOptions) { + info( + logging, + null, + `\n ${magenta(`Check our deployment docs for ${bold(adapter.packageName)} to update your "adapter" config.`)}` + ); +} + async function parseAstroConfig(configURL: URL): Promise { const source = await fs.readFile(fileURLToPath(configURL), { encoding: 'utf-8' }); const result = parse(source); @@ -324,14 +355,14 @@ async function addIntegration(ast: t.File, integration: IntegrationInfo) { }); } -async function setAdapter(ast: t.File, adapter: IntegrationInfo) { +async function setAdapter(ast: t.File, adapter: IntegrationInfo, exportName: string) { const adapterId = t.identifier(toIdent(adapter.id)); ensureImport( ast, t.importDeclaration( [t.importDefaultSpecifier(adapterId)], - t.stringLiteral(adapter.packageName) + t.stringLiteral(exportName) ) ); @@ -380,11 +411,13 @@ async function updateAstroConfig({ ast, flags, logging, + logAdapterInstructions, }: { configURL: URL; ast: t.File; flags: yargs.Arguments; logging: LogOptions; + logAdapterInstructions: boolean; }): Promise { const input = await fs.readFile(fileURLToPath(configURL), { encoding: 'utf-8' }); let output = await generate(ast); @@ -432,6 +465,14 @@ async function updateAstroConfig({ `\n ${magenta('Astro will make the following changes to your config file:')}\n${message}` ); + if (logAdapterInstructions) { + info( + logging, + null, + magenta(` For complete deployment options, visit\n ${bold('https://docs.astro.build/en/guides/deploy/')}\n`) + ); + } + if (await askToContinue({ flags })) { await fs.writeFile(fileURLToPath(configURL), output, { encoding: 'utf-8' }); debug('add', `Updated astro config`); From 68266503b41310d071df3fe22e383dff1037b5ea Mon Sep 17 00:00:00 2001 From: bholmesdev Date: Fri, 8 Jul 2022 16:26:54 -0400 Subject: [PATCH 13/13] refactor: inline adapter config log --- packages/astro/src/core/add/index.ts | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/packages/astro/src/core/add/index.ts b/packages/astro/src/core/add/index.ts index 25b093b75e40..dde67065312f 100644 --- a/packages/astro/src/core/add/index.ts +++ b/packages/astro/src/core/add/index.ts @@ -139,7 +139,11 @@ export default async function add(names: string[], { cwd, flags, logging, teleme if (officialExportName) { await setAdapter(ast, integration, officialExportName); } else { - logAdapterConfigInstructions(integration, logging); + info( + logging, + null, + `\n ${magenta(`Check our deployment docs for ${bold(integration.packageName)} to update your "adapter" config.`)}` + ); } } else { await addIntegration(ast, integration); @@ -264,14 +268,6 @@ function isAdapter(integration: IntegrationInfo): integration is IntegrationInfo return integration.type === 'adapter'; } -function logAdapterConfigInstructions(adapter: (IntegrationInfo & { type: 'adapter' }), logging: LogOptions) { - info( - logging, - null, - `\n ${magenta(`Check our deployment docs for ${bold(adapter.packageName)} to update your "adapter" config.`)}` - ); -} - async function parseAstroConfig(configURL: URL): Promise { const source = await fs.readFile(fileURLToPath(configURL), { encoding: 'utf-8' }); const result = parse(source);