From ea9b3dd72b98b3f5a542ca24a275f673faa6c7c5 Mon Sep 17 00:00:00 2001 From: Bjorn Lu Date: Fri, 17 Mar 2023 01:04:26 +0800 Subject: [PATCH] Support tsconfig aliases in styles (#6566) --- .changeset/empty-ravens-complain.md | 5 + .../src/vite-plugin-config-alias/index.ts | 116 +++++++----------- packages/astro/test/alias-tsconfig.test.js | 15 +++ .../alias-tsconfig/src/components/Foo.astro | 1 + .../alias-tsconfig/src/components/Style.astro | 2 + .../alias-tsconfig/src/pages/index.astro | 5 + .../alias-tsconfig/src/styles/extra.css | 3 + .../alias-tsconfig/src/styles/main.css | 5 + .../fixtures/alias-tsconfig/tsconfig.json | 9 +- 9 files changed, 81 insertions(+), 80 deletions(-) create mode 100644 .changeset/empty-ravens-complain.md create mode 100644 packages/astro/test/fixtures/alias-tsconfig/src/components/Foo.astro create mode 100644 packages/astro/test/fixtures/alias-tsconfig/src/components/Style.astro create mode 100644 packages/astro/test/fixtures/alias-tsconfig/src/styles/extra.css create mode 100644 packages/astro/test/fixtures/alias-tsconfig/src/styles/main.css diff --git a/.changeset/empty-ravens-complain.md b/.changeset/empty-ravens-complain.md new file mode 100644 index 000000000000..8078824374ab --- /dev/null +++ b/.changeset/empty-ravens-complain.md @@ -0,0 +1,5 @@ +--- +'astro': patch +--- + +Support tsconfig aliases in styles diff --git a/packages/astro/src/vite-plugin-config-alias/index.ts b/packages/astro/src/vite-plugin-config-alias/index.ts index ab4547cc671a..52325904a1f8 100644 --- a/packages/astro/src/vite-plugin-config-alias/index.ts +++ b/packages/astro/src/vite-plugin-config-alias/index.ts @@ -1,47 +1,20 @@ -import * as path from 'path'; +import fs from 'fs'; +import path from 'path'; import type { AstroSettings } from '../@types/astro'; -import type * as vite from 'vite'; - -/** Result of successfully parsed tsconfig.json or jsconfig.json. */ -export declare interface Alias { - find: RegExp; - replacement: string; -} - -/** Returns a path with its slashes replaced with posix slashes. */ -const normalize = (pathname: string) => String(pathname).split(path.sep).join(path.posix.sep); +import type { Alias, Plugin as VitePlugin } from 'vite'; +import type { TsConfigJson } from 'tsconfig-resolver'; +import slash from 'slash'; /** Returns a list of compiled aliases. */ -const getConfigAlias = (settings: AstroSettings): Alias[] | null => { - /** Closest tsconfig.json or jsconfig.json */ - const config = settings.tsConfig; - const configPath = settings.tsConfigPath; - - // if no config was found, return null - if (!config || !configPath) return null; - - /** Compiler options from tsconfig.json or jsconfig.json. */ - const compilerOptions = Object(config.compilerOptions); - - // if no compilerOptions.baseUrl was defined, return null - if (!compilerOptions.baseUrl) return null; - - // resolve the base url from the configuration file directory - const baseUrl = path.posix.resolve( - path.posix.dirname(normalize(configPath).replace(/^\/?/, '/')), - normalize(compilerOptions.baseUrl) - ); - - /** List of compiled alias expressions. */ +const getConfigAlias = ( + paths: NonNullable, + baseUrl: NonNullable +): Alias[] => { const aliases: Alias[] = []; // compile any alias expressions and push them to the list - for (let [alias, values] of Object.entries( - Object(compilerOptions.paths) as { [key: string]: string[] } - )) { - values = [].concat(values as never); - + for (const [alias, values] of Object.entries(paths)) { /** Regular Expression used to match a given path. */ const find = new RegExp( `^${[...alias] @@ -54,7 +27,7 @@ const getConfigAlias = (settings: AstroSettings): Alias[] | null => { /** Internal index used to calculate the matching id in a replacement. */ let matchId = 0; - for (let value of values) { + for (const value of values) { /** String used to replace a matched path. */ const replacement = [...path.posix.resolve(baseUrl, value)] .map((segment) => (segment === '*' ? `$${++matchId}` : segment === '$' ? '$$' : segment)) @@ -64,14 +37,6 @@ const getConfigAlias = (settings: AstroSettings): Alias[] | null => { } } - // compile the baseUrl expression and push it to the list - // - `baseUrl` changes the way non-relative specifiers are resolved - // - if `baseUrl` exists then all non-relative specifiers are resolved relative to it - aliases.push({ - find: /^(?!\.*\/)(.+)$/, - replacement: `${[...baseUrl].map((segment) => (segment === '$' ? '$$' : segment)).join('')}/$1`, - }); - return aliases; }; @@ -80,39 +45,42 @@ export default function configAliasVitePlugin({ settings, }: { settings: AstroSettings; -}): vite.PluginOption { - const { config } = settings; - /** Aliases from the tsconfig.json or jsconfig.json configuration. */ - const configAlias = getConfigAlias(settings); +}): VitePlugin | null { + const { tsConfig, tsConfigPath } = settings; + if (!tsConfig || !tsConfigPath || !tsConfig.compilerOptions) return null; + + const { baseUrl, paths } = tsConfig.compilerOptions; + if (!baseUrl || !paths) return null; + + // resolve the base url from the configuration file directory + const resolvedBaseUrl = path.posix.resolve( + path.posix.dirname(slash(tsConfigPath).replace(/^\/?/, '/')), + slash(baseUrl) + ); - // if no config alias was found, bypass this plugin - if (!configAlias) return {} as vite.PluginOption; + const configAlias = getConfigAlias(paths, resolvedBaseUrl); return { name: 'astro:tsconfig-alias', enforce: 'pre', - async resolveId(sourceId: string, importer, options) { - /** Resolved ID conditionally handled by any other resolver. (this gives priority to all other resolvers) */ - const resolvedId = await this.resolve(sourceId, importer, { skipSelf: true, ...options }); - - // if any other resolver handles the file, return that resolution - if (resolvedId) return resolvedId; - - // conditionally resolve the source ID from any matching alias or baseUrl - for (const alias of configAlias) { - if (alias.find.test(sourceId)) { - /** Processed Source ID with our alias applied. */ - const aliasedSourceId = sourceId.replace(alias.find, alias.replacement); - - /** Resolved ID conditionally handled by any other resolver. (this also gives priority to all other resolvers) */ - const resolvedAliasedId = await this.resolve(aliasedSourceId, importer, { - skipSelf: true, - ...options, - }); - - // if the existing resolvers find the file, return that resolution - if (resolvedAliasedId) return resolvedAliasedId; - } + config() { + if (configAlias.length) { + return { + resolve: { + alias: configAlias, + }, + }; + } + }, + resolveId(id) { + if (id.startsWith('.') || id.startsWith('/')) return; + + // Handle baseUrl mapping for non-relative and non-root imports. + // Since TypeScript only applies `baseUrl` autocompletions for files that exist + // in the filesystem only, we can use this heuristic to skip resolve if needed. + const resolved = path.posix.join(resolvedBaseUrl, id); + if (fs.existsSync(resolved)) { + return resolved; } }, }; diff --git a/packages/astro/test/alias-tsconfig.test.js b/packages/astro/test/alias-tsconfig.test.js index 1468aabeb52d..c6aa982849fd 100644 --- a/packages/astro/test/alias-tsconfig.test.js +++ b/packages/astro/test/alias-tsconfig.test.js @@ -34,5 +34,20 @@ describe('Aliases with tsconfig.json', () => { const scripts = $('script').toArray(); expect(scripts.length).to.be.greaterThan(0); }); + + it('can load via baseUrl', async () => { + const html = await fixture.fetch('/').then((res) => res.text()); + const $ = cheerio.load(html); + + expect($('#foo').text()).to.equal('foo'); + }); + + it('works in css @import', async () => { + const html = await fixture.fetch('/').then((res) => res.text()); + console.log(html) + // imported css should be bundled + expect(html).to.include('#style-red'); + expect(html).to.include('#style-blue'); + }); }); }); diff --git a/packages/astro/test/fixtures/alias-tsconfig/src/components/Foo.astro b/packages/astro/test/fixtures/alias-tsconfig/src/components/Foo.astro new file mode 100644 index 000000000000..42bd5c2a5349 --- /dev/null +++ b/packages/astro/test/fixtures/alias-tsconfig/src/components/Foo.astro @@ -0,0 +1 @@ +

foo

\ No newline at end of file diff --git a/packages/astro/test/fixtures/alias-tsconfig/src/components/Style.astro b/packages/astro/test/fixtures/alias-tsconfig/src/components/Style.astro new file mode 100644 index 000000000000..0d8e06ae3430 --- /dev/null +++ b/packages/astro/test/fixtures/alias-tsconfig/src/components/Style.astro @@ -0,0 +1,2 @@ +

i am blue

+

i am red

diff --git a/packages/astro/test/fixtures/alias-tsconfig/src/pages/index.astro b/packages/astro/test/fixtures/alias-tsconfig/src/pages/index.astro index c00b9f0836be..20727cd6edec 100644 --- a/packages/astro/test/fixtures/alias-tsconfig/src/pages/index.astro +++ b/packages/astro/test/fixtures/alias-tsconfig/src/pages/index.astro @@ -1,5 +1,8 @@ --- import Client from '@components/Client.svelte' +import Foo from 'src/components/Foo.astro'; +import StyleComp from 'src/components/Style.astro'; +import '@styles/main.css' --- @@ -10,6 +13,8 @@ import Client from '@components/Client.svelte'
+ +
diff --git a/packages/astro/test/fixtures/alias-tsconfig/src/styles/extra.css b/packages/astro/test/fixtures/alias-tsconfig/src/styles/extra.css new file mode 100644 index 000000000000..c2e822c5d542 --- /dev/null +++ b/packages/astro/test/fixtures/alias-tsconfig/src/styles/extra.css @@ -0,0 +1,3 @@ +#style-red { + color: red; +} diff --git a/packages/astro/test/fixtures/alias-tsconfig/src/styles/main.css b/packages/astro/test/fixtures/alias-tsconfig/src/styles/main.css new file mode 100644 index 000000000000..77e2f77db6b7 --- /dev/null +++ b/packages/astro/test/fixtures/alias-tsconfig/src/styles/main.css @@ -0,0 +1,5 @@ +@import "@styles/extra.css"; + +#style-blue { + color: blue; +} diff --git a/packages/astro/test/fixtures/alias-tsconfig/tsconfig.json b/packages/astro/test/fixtures/alias-tsconfig/tsconfig.json index 01dd88abee9c..0e86e124939a 100644 --- a/packages/astro/test/fixtures/alias-tsconfig/tsconfig.json +++ b/packages/astro/test/fixtures/alias-tsconfig/tsconfig.json @@ -5,12 +5,9 @@ "@components/*": [ "src/components/*" ], - "@layouts/*": [ - "src/layouts/*" - ], - "@assets/*": [ - "src/assets/*" - ], + "@styles/*": [ + "src/styles/*" + ] } } }