From 78596ce7dcf644db40c92bdfcfd14222553ea71b Mon Sep 17 00:00:00 2001 From: Matthew Phillips Date: Wed, 2 Mar 2022 15:18:41 -0500 Subject: [PATCH 1/4] Move lit test to the static build --- packages/astro/test/lit-element.test.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/astro/test/lit-element.test.js b/packages/astro/test/lit-element.test.js index 8c333bca2422..395eaf2e52ac 100644 --- a/packages/astro/test/lit-element.test.js +++ b/packages/astro/test/lit-element.test.js @@ -18,9 +18,6 @@ describe('LitElement test', function () { fixture = await loadFixture({ projectRoot: './fixtures/lit-element/', renderers: ['@astrojs/renderer-lit'], - buildOptions: { - legacyBuild: true - } }); await fixture.build(); }); From 135e9f778a8176b9562330ec30c90c0878148bd5 Mon Sep 17 00:00:00 2001 From: Matthew Phillips Date: Thu, 3 Mar 2022 10:33:17 -0500 Subject: [PATCH 2/4] Migrate astro-env plugin to work in the static build --- packages/astro/src/vite-plugin-env/index.ts | 71 +++++++++++++++------ packages/astro/test/astro-envs.test.js | 9 ++- 2 files changed, 57 insertions(+), 23 deletions(-) diff --git a/packages/astro/src/vite-plugin-env/index.ts b/packages/astro/src/vite-plugin-env/index.ts index c216b7111857..761dd5c330c8 100644 --- a/packages/astro/src/vite-plugin-env/index.ts +++ b/packages/astro/src/vite-plugin-env/index.ts @@ -1,5 +1,6 @@ import type * as vite from 'vite'; import type { AstroConfig } from '../@types/astro'; +import type { TransformPluginContext } from 'rollup'; import MagicString from 'magic-string'; import { fileURLToPath } from 'url'; import { loadEnv } from 'vite'; @@ -30,7 +31,7 @@ function getPrivateEnv(viteConfig: vite.ResolvedConfig, astroConfig: AstroConfig if (privateKeys.length === 0) { return null; } - return Object.fromEntries(privateKeys.map((key) => [key, fullEnv[key]])); + return Object.fromEntries(privateKeys.map((key) => [key, JSON.stringify(fullEnv[key])])); } function referencesPrivateKey(source: string, privateEnv: Record) { @@ -43,41 +44,75 @@ function referencesPrivateKey(source: string, privateEnv: Record) { export default function envVitePlugin({ config: astroConfig }: EnvPluginOptions): vite.PluginOption { let privateEnv: Record | null; let config: vite.ResolvedConfig; + let viteDefine: vite.Plugin | undefined; + let replacements: Record; + let pattern: RegExp | undefined; + function callViteDefine(thisValue:TransformPluginContext, source: string, id: string, options: { + ssr?: boolean | undefined; +} | undefined) { + return viteDefine?.transform?.call(thisValue, source, id, options) || source; +} return { name: 'astro:vite-plugin-env', enforce: 'pre', configResolved(resolvedConfig) { config = resolvedConfig; - if (config.envPrefix) { + viteDefine = config.plugins.find(plugin => plugin.name === 'vite:define'); + if(viteDefine) { + const index = config.plugins.indexOf(viteDefine); + (config.plugins as vite.Plugin[]).splice(index, 1); } }, async transform(source, id, options) { const ssr = options?.ssr === true; - if (!ssr) return source; - if (!source.includes('import.meta')) return source; - if (!/\benv\b/.test(source)) return source; + + if(!ssr) { + return callViteDefine(this, source, id, options); + } + + if(!source.includes('import.meta') || !/\benv\b/.test(source)) { + return source; + } if (typeof privateEnv === 'undefined') { privateEnv = getPrivateEnv(config, astroConfig); + if(privateEnv) { + const entries = Object.entries(privateEnv).map(([key, value]) => ([`import.meta.env.${key}`, value])); + replacements = Object.fromEntries(entries); + pattern = new RegExp( + // Do not allow preceding '.', but do allow preceding '...' for spread operations + '(? { + return str.replace(/[-[\]/{}()*+?.\\^$|]/g, '\\$&'); + }) + .join('|') + + // prevent trailing assignments + ')\\b(?!\\s*?=[^=])', 'g'); + } + } - if (!privateEnv) return source; - if (!referencesPrivateKey(source, privateEnv)) return source; + if (!privateEnv || !pattern) return callViteDefine(this, source, id, options); + if (!referencesPrivateKey(source, privateEnv)) return callViteDefine(this, source, id, options); + // Find matches for *private* env and do our own replacement. const s = new MagicString(source); - // prettier-ignore - s.prepend(`import.meta.env = new Proxy(import.meta.env, {` + - `get(target, prop, reciever) {` + - `const PRIVATE = ${JSON.stringify(privateEnv)};` + - `if (typeof PRIVATE[prop] !== 'undefined') {` + - `return PRIVATE[prop];` + - `}` + - `return Reflect.get(target, prop, reciever);` + - `}` + - `});\n`); + let hasReplaced = false + let match: RegExpExecArray | null + + while ((match = pattern.exec(source))) { + hasReplaced = true + const start = match.index + const end = start + match[0].length + const replacement = '' + replacements[match[1]] + s.overwrite(start, end, replacement) + } - return s.toString(); + let code = s.toString(); + // Call back to vite:define to do public replacements. + return callViteDefine(this, code, id, options); }, }; } diff --git a/packages/astro/test/astro-envs.test.js b/packages/astro/test/astro-envs.test.js index f9056b416362..3f4720c33090 100644 --- a/packages/astro/test/astro-envs.test.js +++ b/packages/astro/test/astro-envs.test.js @@ -7,7 +7,6 @@ describe('Environment Variables', () => { before(async () => { fixture = await loadFixture({ projectRoot: './fixtures/astro-envs/', - buildOptions: { legacyBuild: true } // TODO make this test work without legacyBuild }); await fixture.build(); @@ -25,7 +24,7 @@ describe('Environment Variables', () => { }); it('includes public env in client-side JS', async () => { - let dirs = await fixture.readdir('/assets'); + let dirs = await fixture.readdir('/'); let found = false; // Look in all of the .js files to see if the public env is inlined. @@ -34,7 +33,7 @@ describe('Environment Variables', () => { await Promise.all( dirs.map(async (path) => { if (path.endsWith('.js')) { - let js = await fixture.readFile(`/assets/${path}`); + let js = await fixture.readFile(`/${path}`); if (js.includes('BLUE_BAYOU')) { found = true; } @@ -46,7 +45,7 @@ describe('Environment Variables', () => { }); it('does not include private env in client-side JS', async () => { - let dirs = await fixture.readdir('/assets'); + let dirs = await fixture.readdir('/'); let found = false; // Look in all of the .js files to see if the public env is inlined. @@ -55,7 +54,7 @@ describe('Environment Variables', () => { await Promise.all( dirs.map(async (path) => { if (path.endsWith('.js')) { - let js = await fixture.readFile(`/assets/${path}`); + let js = await fixture.readFile(`/${path}`); if (js.includes('CLUB_33')) { found = true; } From da653440a06ce40536238d5d0b41ce3e1ba5be35 Mon Sep 17 00:00:00 2001 From: Matthew Phillips Date: Thu, 3 Mar 2022 12:34:03 -0500 Subject: [PATCH 3/4] Do not remove vite:define --- packages/astro/src/vite-plugin-env/index.ts | 41 ++++++--------------- 1 file changed, 12 insertions(+), 29 deletions(-) diff --git a/packages/astro/src/vite-plugin-env/index.ts b/packages/astro/src/vite-plugin-env/index.ts index 761dd5c330c8..36a9835b1f55 100644 --- a/packages/astro/src/vite-plugin-env/index.ts +++ b/packages/astro/src/vite-plugin-env/index.ts @@ -44,32 +44,19 @@ function referencesPrivateKey(source: string, privateEnv: Record) { export default function envVitePlugin({ config: astroConfig }: EnvPluginOptions): vite.PluginOption { let privateEnv: Record | null; let config: vite.ResolvedConfig; - let viteDefine: vite.Plugin | undefined; let replacements: Record; let pattern: RegExp | undefined; - function callViteDefine(thisValue:TransformPluginContext, source: string, id: string, options: { - ssr?: boolean | undefined; -} | undefined) { - return viteDefine?.transform?.call(thisValue, source, id, options) || source; -} return { name: 'astro:vite-plugin-env', enforce: 'pre', - configResolved(resolvedConfig) { config = resolvedConfig; - viteDefine = config.plugins.find(plugin => plugin.name === 'vite:define'); - if(viteDefine) { - const index = config.plugins.indexOf(viteDefine); - (config.plugins as vite.Plugin[]).splice(index, 1); - } }, - async transform(source, id, options) { const ssr = options?.ssr === true; if(!ssr) { - return callViteDefine(this, source, id, options); + return source; } if(!source.includes('import.meta') || !/\benv\b/.test(source)) { @@ -92,27 +79,23 @@ export default function envVitePlugin({ config: astroConfig }: EnvPluginOptions) // prevent trailing assignments ')\\b(?!\\s*?=[^=])', 'g'); } - } - if (!privateEnv || !pattern) return callViteDefine(this, source, id, options); - if (!referencesPrivateKey(source, privateEnv)) return callViteDefine(this, source, id, options); + + if (!privateEnv || !pattern) return source; + if (!referencesPrivateKey(source, privateEnv)) return source; // Find matches for *private* env and do our own replacement. const s = new MagicString(source); - let hasReplaced = false - let match: RegExpExecArray | null + let match: RegExpExecArray | null - while ((match = pattern.exec(source))) { - hasReplaced = true - const start = match.index - const end = start + match[0].length - const replacement = '' + replacements[match[1]] - s.overwrite(start, end, replacement) - } + while ((match = pattern.exec(source))) { + const start = match.index + const end = start + match[0].length + const replacement = '' + replacements[match[1]] + s.overwrite(start, end, replacement) + } - let code = s.toString(); - // Call back to vite:define to do public replacements. - return callViteDefine(this, code, id, options); + return s.toString(); }, }; } From 65a9669c7e2690134bdacc8cb9e5de93b9867e4f Mon Sep 17 00:00:00 2001 From: Matthew Phillips Date: Thu, 3 Mar 2022 12:59:44 -0500 Subject: [PATCH 4/4] Adds a changeset --- .changeset/fresh-ladybugs-think.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/fresh-ladybugs-think.md diff --git a/.changeset/fresh-ladybugs-think.md b/.changeset/fresh-ladybugs-think.md new file mode 100644 index 000000000000..bdf29ac191e0 --- /dev/null +++ b/.changeset/fresh-ladybugs-think.md @@ -0,0 +1,5 @@ +--- +'astro': patch +--- + +Fixes use of private .env variables with the static build