Resolve component URLs during compilation (#40)

Previously dynamic component URLs were being resolved client-side in a weird way that only worked during dev. This change makes them handle during compilation, so it works in both (and improves readability of the dynamic import output).
This commit is contained in:
Matthew Phillips 2021-03-30 15:06:43 -04:00 committed by GitHub
parent 7334a550d8
commit 4a732837cd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 28 additions and 16 deletions

View file

@ -3,7 +3,7 @@ const { readFile } = require('fs').promises;
// Snowpack plugins must be CommonJS :(
const transformPromise = import('./lib/compiler/index.js');
module.exports = function (snowpackConfig, { resolve, extensions } = {}) {
module.exports = function (snowpackConfig, { resolve, extensions, astroConfig } = {}) {
return {
name: 'snowpack-astro',
knownEntrypoints: ['deepmerge'],
@ -16,6 +16,7 @@ module.exports = function (snowpackConfig, { resolve, extensions } = {}) {
const projectRoot = snowpackConfig.root;
const contents = await readFile(filePath, 'utf-8');
const compileOptions = {
astroConfig,
resolve,
extensions,
};

View file

@ -1,8 +1,9 @@
import type { LogOptions } from '../logger';
import type { ValidExtensionPlugins } from './astro';
import type { AstroConfig, ValidExtensionPlugins } from './astro';
export interface CompileOptions {
logging: LogOptions;
resolve: (p: string) => Promise<string>;
astroConfig: AstroConfig;
extensions?: Record<string, ValidExtensionPlugins>;
}

View file

@ -1,5 +1,5 @@
import type { CompileOptions } from '../@types/compiler';
import type { ValidExtensionPlugins } from '../@types/astro';
import type { AstroConfig, ValidExtensionPlugins } from '../@types/astro';
import type { Ast, TemplateNode } from '../parser/interfaces';
import type { JsxItem, TransformResult } from '../@types/astro';
@ -111,14 +111,25 @@ const defaultExtensions: Readonly<Record<string, ValidExtensionPlugins>> = {
type DynamicImportMap = Map<'vue' | 'react' | 'react-dom' | 'preact', string>;
function getComponentWrapper(_name: string, { type, plugin, url }: ComponentInfo, dynamicImports: DynamicImportMap) {
interface GetComponentWrapperOptions {
filename: string;
astroConfig: AstroConfig;
dynamicImports: DynamicImportMap;
}
function getComponentWrapper(_name: string, { type, plugin, url }: ComponentInfo, opts: GetComponentWrapperOptions) {
const { astroConfig, dynamicImports, filename } = opts;
const { astroRoot } = astroConfig;
const [name, kind] = _name.split(':');
const currFileUrl = new URL(`file://${filename}`);
if (!plugin) {
throw new Error(`No supported plugin found for extension ${type}`);
}
const getComponentUrl = (ext = '.js') => `new URL(${JSON.stringify(url.replace(/\.[^.]+$/, ext))}, \`http://TEST\${import.meta.url\}\`).pathname.replace(/^\\/\\//, '/_astro/')`;
const getComponentUrl = (ext = '.js') => {
const outUrl = new URL(url, currFileUrl);
return '/_astro/' + path.posix.relative(astroRoot.pathname, outUrl.pathname).replace(/\.[^.]+$/, ext);
};
switch (plugin) {
case 'astro': {
@ -242,8 +253,8 @@ async function acquireDynamicComponentImports(plugins: Set<ValidExtensionPlugins
return importMap;
}
export async function codegen(ast: Ast, { compileOptions }: CodeGenOptions): Promise<TransformResult> {
const { extensions = defaultExtensions } = compileOptions;
export async function codegen(ast: Ast, { compileOptions, filename }: CodeGenOptions): Promise<TransformResult> {
const { extensions = defaultExtensions, astroConfig } = compileOptions;
await eslexer.init;
const componentImports: ImportDeclaration[] = [];
@ -343,7 +354,7 @@ export async function codegen(ast: Ast, { compileOptions }: CodeGenOptions): Pro
if (!components[componentName]) {
throw new Error(`Unknown Component: ${componentName}`);
}
const { wrapper, wrapperImport } = getComponentWrapper(name, components[componentName], dynamicImports);
const { wrapper, wrapperImport } = getComponentWrapper(name, components[componentName], {astroConfig, dynamicImports, filename});
if (wrapperImport) {
importExportStatements.add(wrapperImport);
}
@ -395,7 +406,7 @@ export async function codegen(ast: Ast, { compileOptions }: CodeGenOptions): Pro
if (!componentImportData) {
throw new Error(`Unknown Component: ${componentName}`);
}
const { wrapper, wrapperImport } = getComponentWrapper(name, components[componentName], dynamicImports);
const { wrapper, wrapperImport } = getComponentWrapper(name, components[componentName], {astroConfig, dynamicImports, filename});
if (wrapperImport) {
importExportStatements.add(wrapperImport);
}

View file

@ -1,4 +1,5 @@
import type { LogOptions } from '../logger.js';
import type { AstroConfig } from '../@types/astro';
import path from 'path';
import micromark from 'micromark';
@ -14,15 +15,11 @@ import { optimize } from './optimize/index.js';
import { codegen } from './codegen.js';
interface CompileOptions {
astroConfig: AstroConfig;
logging: LogOptions;
resolve: (p: string) => Promise<string>;
}
const defaultCompileOptions: CompileOptions = {
logging: defaultLogOptions,
resolve: (p: string) => Promise.resolve(p),
};
function internalImport(internalPath: string) {
return `/_astro_internal/${internalPath}`;
}
@ -107,7 +104,7 @@ async function transformFromSource(
export async function compileComponent(
source: string,
{ compileOptions = defaultCompileOptions, filename, projectRoot }: { compileOptions: CompileOptions; filename: string; projectRoot: string }
{ compileOptions, filename, projectRoot }: { compileOptions: CompileOptions; filename: string; projectRoot: string }
): Promise<CompileResult> {
const sourceJsx = await transformFromSource(source, { compileOptions, filename, projectRoot });
const isPage = path.extname(filename) === '.md' || sourceJsx.items.some((item) => item.name === 'html');

View file

@ -23,7 +23,7 @@ export function createRenderer(renderer: Renderer) {
return [...acc, `import("${context.frameworkUrls[lib as any]}")`];
}, [])
.join(',');
return `const [{${context.componentExport}: Component}, ${values}] = await Promise.all([import(${context.componentUrl})${renderer.imports ? ', ' + libs : ''}]);`;
return `const [{${context.componentExport}: Component}, ${values}] = await Promise.all([import("${context.componentUrl}")${renderer.imports ? ', ' + libs : ''}]);`;
};
const serializeProps = (props: Record<string, any>) => JSON.stringify(props);
const createContext = () => {

View file

@ -126,7 +126,9 @@ export async function createRuntime(astroConfig: AstroConfig, { logging }: Runti
const astroPlugOptions: {
resolve?: (s: string) => Promise<string>;
extensions?: Record<string, string>;
astroConfig: AstroConfig;
} = {
astroConfig,
extensions,
resolve: async (pkgName: string) => snowpack.getUrlForPackage(pkgName),
};