Refactor astro export map, add source-map-support (#161)

* fix: add svelte plugin for esbuild, remove precompiled svelte components

* refactor: public export map, public types

* feat: add source-map-support to common code paths

* chore: move new "exports" to "imports" map, add internal types

* Include outPath in error logging for bad load status, and drop error stack (#163)

* Include outPath in the error logging for bad load status

* Discard error stack since it seems not useful

* feat: improve build error logging

* refactor: use object param for writeResult

Co-authored-by: Kevin (Kun) "Kassimo" Qian <kevinkassimo@gmail.com>
This commit is contained in:
Nate Moore 2021-05-03 13:18:08 -05:00 committed by GitHub
parent 4ff3add50f
commit c93201a909
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
29 changed files with 167 additions and 227 deletions

View file

@ -36,13 +36,10 @@
"lerna": "^4.0.0",
"prettier": "^2.2.1",
"tiny-glob": "^0.2.8",
"esbuild": "^0.11.17",
"svelte": "^3.38.0",
"typescript": "^4.2.4",
"uvu": "^0.5.1",
"eslint": "^7.22.0",
"eslint-config-prettier": "^8.1.0",
"eslint-plugin-prettier": "^3.3.1",
"@typescript-eslint/eslint-plugin": "^4.18.0",
"@typescript-eslint/parser": "^4.18.0"
"uvu": "^0.5.1"
},
"dependencies": {
"@changesets/cli": "^2.16.0"

27
packages/astro/internal.d.ts vendored Normal file
View file

@ -0,0 +1,27 @@
declare module '#astro/compiler' {
export * from 'astro/dist/types/compiler';
}
declare module '#astro/ast' {
export * from 'astro/dist/types/ast';
}
declare module '#astro/build' {
export * from 'astro/dist/types/build';
}
declare module '#astro/cli' {
export * from 'astro/dist/types/cli';
}
declare module '#astro/config' {
export * from 'astro/dist/types/config';
}
declare module '#astro/dev' {
export * from 'astro/dist/types/dev';
}
declare module '#astro/logger' {
export * from 'astro/dist/types/logger';
}
declare module '#astro/runtime' {
export * from 'astro/dist/types/runtime';
}
declare module '#astro/search' {
export * from 'astro/dist/types/search';
}

View file

@ -4,11 +4,16 @@
"author": "Skypack",
"license": "MIT",
"type": "module",
"types": "./dist/types",
"exports": {
".": "./astro.mjs",
"./snowpack-plugin": "./snowpack-plugin.cjs",
"./components/*.astro": "./components/*.astro",
"./runtime/svelte": "./runtime/svelte.js"
"./runtime/svelte": "./dist/frontend/runtime/svelte.js"
},
"imports": {
"#astro/compiler": "./dist/compiler/index.js",
"#astro/*": "./dist/*.js"
},
"bin": {
"astro": "astro.mjs"
@ -22,7 +27,7 @@
"astro.mjs"
],
"scripts": {
"build": "astro-scripts build 'src/**/*.ts' && tsc -p tsconfig.json",
"build": "astro-scripts build 'src/*.ts' 'src/compiler/index.ts' 'src/frontend/**/*.ts' && tsc",
"postbuild": "astro-scripts copy 'src/**/*.astro'",
"dev": "astro-scripts dev 'src/**/*.ts'",
"test": "uvu test -i fixtures -i test-utils.js"
@ -72,6 +77,7 @@
"rollup-plugin-terser": "^7.0.2",
"sass": "^1.32.8",
"snowpack": "^3.3.7",
"source-map-support": "^0.5.19",
"svelte": "^3.35.0",
"unified": "^9.2.1",
"vue": "^3.0.10",

View file

@ -1,4 +0,0 @@
import SvelteRuntime from '../lib/frontend/runtime/svelte.js';
Function.prototype(SvelteRuntime);
export default SvelteRuntime;

View file

@ -1,3 +1,4 @@
import 'source-map-support/register.js';
import type { Attribute } from 'astro-parser';
// AST utility functions

View file

@ -1,3 +1,4 @@
import 'source-map-support/register.js';
import type { AstroConfig, RuntimeMode } from './@types/astro';
import type { LogOptions } from './logger';
import type { AstroRuntime, LoadResult } from './runtime';
@ -15,7 +16,7 @@ import { generateRSS } from './build/rss.js';
import { generateSitemap } from './build/sitemap.js';
import { collectStatics } from './build/static.js';
import { canonicalURL } from './build/util.js';
import { pathToFileURL } from 'node:url';
const { mkdir, readFile, writeFile } = fsPromises;
@ -65,12 +66,19 @@ async function writeFilep(outPath: URL, bytes: string | Buffer, encoding: 'utf8'
await writeFile(outPath, bytes, encoding || 'binary');
}
interface WriteResultOptions {
srcPath: string;
result: LoadResult;
outPath: URL,
encoding: null|'utf8'
}
/** Utility for writing a build result to disk */
async function writeResult(result: LoadResult, outPath: URL, encoding: null | 'utf8') {
async function writeResult({ srcPath, result, outPath, encoding }: WriteResultOptions) {
if (result.statusCode === 500 || result.statusCode === 404) {
error(logging, 'build', result.error || result.statusCode);
error(logging, 'build', ` Failed to build ${srcPath}\n${' '.repeat(9)}`, result.error?.message ?? `Unexpected load result (${result.statusCode})`);
} else if (result.statusCode !== 200) {
error(logging, 'build', `Unexpected load result (${result.statusCode}) for ${fileURLToPath(outPath)}`);
error(logging, 'build', ` Failed to build ${srcPath}\n${' '.repeat(9)}`, `Unexpected load result (${result.statusCode}) for ${fileURLToPath(outPath)}`);
} else {
const bytes = result.contents;
await writeFilep(outPath, bytes, encoding);
@ -87,6 +95,7 @@ function getPageType(filepath: URL): 'collection' | 'static' {
async function buildCollectionPage({ astroRoot, dist, filepath, runtime, site, statics }: PageBuildOptions): Promise<PageResult> {
const rel = path.relative(fileURLToPath(astroRoot) + '/pages', fileURLToPath(filepath)); // pages/index.astro
const pagePath = `/${rel.replace(/\$([^.]+)\.astro$/, '$1')}`;
const srcPath = fileURLToPath(new URL('pages/' + rel, astroRoot));
const builtURLs = new Set<string>(); // !important: internal cache that prevents building the same URLs
/** Recursively build collection URLs */
@ -96,7 +105,7 @@ async function buildCollectionPage({ astroRoot, dist, filepath, runtime, site, s
builtURLs.add(url);
if (result.statusCode === 200) {
const outPath = new URL('./' + url + '/index.html', dist);
await writeResult(result, outPath, 'utf8');
await writeResult({ srcPath, result, outPath, encoding: 'utf8' });
mergeSet(statics, collectStatics(result.contents.toString('utf8')));
}
return result;
@ -152,10 +161,11 @@ async function buildStaticPage({ astroRoot, dist, filepath, runtime, sitemap, st
relPath = relPath.replace(/\.html$/, '/index.html');
}
const srcPath = fileURLToPath(new URL('pages/' + rel, astroRoot));
const outPath = new URL(relPath, dist);
const result = await runtime.load(pagePath);
await writeResult(result, outPath, 'utf8');
await writeResult({ srcPath, result, outPath, encoding: 'utf8' });
if (result.statusCode === 200) {
mergeSet(statics, collectStatics(result.contents.toString('utf8')));
@ -199,9 +209,8 @@ export async function build(astroConfig: AstroConfig): Promise<0 | 1> {
const pages = await allPages(pageRoot);
let builtURLs: string[] = [];
try {
info(logging , 'build', yellow('! building pages...'));
info(logging, 'build', yellow('! building pages...'));
// Vue also console.warns, this silences it.
const release = trapWarn();
await Promise.all(
@ -258,7 +267,7 @@ export async function build(astroConfig: AstroConfig): Promise<0 | 1> {
const outPath = new URL('.' + url, dist);
const result = await runtime.load(url);
await writeResult(result, outPath, null);
await writeResult({ srcPath: url, result, outPath, encoding: null });
}
if (existsSync(astroConfig.public)) {
@ -275,7 +284,7 @@ export async function build(astroConfig: AstroConfig): Promise<0 | 1> {
}
info(logging, 'build', green('✔'), 'public folder copied.');
} else {
if(path.basename(astroConfig.public.toString()) !=='public'){
if (path.basename(astroConfig.public.toString()) !== 'public') {
info(logging, 'tip', yellow(`! no public folder ${astroConfig.public} found...`));
}
}

View file

@ -1,3 +1,4 @@
import 'source-map-support/register.js';
/* eslint-disable no-console */
import type { AstroConfig } from './@types/astro';

View file

@ -1,3 +1,4 @@
import 'source-map-support/register.js';
import type { CompileResult, TransformResult } from '../@types/astro';
import type { CompileOptions } from '../@types/compiler.js';
@ -14,6 +15,8 @@ import { encodeAstroMdx } from './markdown/micromark-mdx-astro.js';
import { transform } from './transform/index.js';
import { codegen } from './codegen/index.js';
export { scopeRule } from './transform/postcss-scoped-styles/index.js'
/** Return Astro internal import URL */
function internalImport(internalPath: string) {
return `/_astro_internal/${internalPath}`;

View file

@ -1,3 +1,4 @@
import 'source-map-support/register.js';
import type { AstroConfig } from './@types/astro';
import { join as pathJoin, resolve as pathResolve } from 'path';
import { existsSync } from 'fs';

View file

@ -1,3 +1,4 @@
import 'source-map-support/register.js';
import type { AstroConfig } from './@types/astro';
import type { LogOptions } from './logger.js';

View file

@ -1,166 +0,0 @@
/* eslint-disable */
// @ts-nocheck
// TODO: don't precompile this, but it works for now
import {
HtmlTag,
SvelteComponentDev,
assign,
claim_component,
create_component,
destroy_component,
detach_dev,
dispatch_dev,
empty,
exclude_internal_props,
get_spread_object,
get_spread_update,
init,
insert_dev,
mount_component,
noop,
not_equal,
transition_in,
transition_out,
validate_slots,
} from 'svelte/internal';
const file = 'App.svelte';
// (5:0) <Component {...props}>
function create_default_slot(ctx) {
let html_tag;
let html_anchor;
const block = {
c: function create() {
html_anchor = empty();
this.h();
},
l: function claim(nodes) {
html_anchor = empty();
this.h();
},
h: function hydrate() {
html_tag = new HtmlTag(html_anchor);
},
m: function mount(target, anchor) {
html_tag.m(/*__astro_children*/ ctx[1], target, anchor);
insert_dev(target, html_anchor, anchor);
},
p: noop,
d: function destroy(detaching) {
if (detaching) detach_dev(html_anchor);
if (detaching) html_tag.d();
},
};
dispatch_dev('SvelteRegisterBlock', {
block,
id: create_default_slot.name,
type: 'slot',
source: '(5:0) <Component {...props}>',
ctx,
});
return block;
}
function create_fragment(ctx) {
let component;
let current;
const component_spread_levels = [/*props*/ ctx[2]];
let component_props = {
$$slots: { default: [create_default_slot] },
$$scope: { ctx },
};
for (let i = 0; i < component_spread_levels.length; i += 1) {
component_props = assign(component_props, component_spread_levels[i]);
}
component = new /*Component*/ ctx[0]({ props: component_props, $$inline: true });
const block = {
c: function create() {
create_component(component.$$.fragment);
},
l: function claim(nodes) {
claim_component(component.$$.fragment, nodes);
},
m: function mount(target, anchor) {
mount_component(component, target, anchor);
current = true;
},
p: function update(ctx, [dirty]) {
const component_changes = dirty & /*props*/ 4 ? get_spread_update(component_spread_levels, [get_spread_object(/*props*/ ctx[2])]) : {};
if (dirty & /*$$scope*/ 16) {
component_changes.$$scope = { dirty, ctx };
}
component.$set(component_changes);
},
i: function intro(local) {
if (current) return;
transition_in(component.$$.fragment, local);
current = true;
},
o: function outro(local) {
transition_out(component.$$.fragment, local);
current = false;
},
d: function destroy(detaching) {
destroy_component(component, detaching);
},
};
dispatch_dev('SvelteRegisterBlock', {
block,
id: create_fragment.name,
type: 'component',
source: '',
ctx,
});
return block;
}
function instance($$self, $$props, $$invalidate) {
let { $$slots: slots = {}, $$scope } = $$props;
validate_slots('App', slots, []);
const { __astro_component: Component, __astro_children, ...props } = $$props;
$$self.$$set = ($$new_props) => {
$$invalidate(3, ($$props = assign(assign({}, $$props), exclude_internal_props($$new_props))));
};
$$self.$capture_state = () => ({ Component, __astro_children, props });
$$self.$inject_state = ($$new_props) => {
$$invalidate(3, ($$props = assign(assign({}, $$props), $$new_props)));
};
if ($$props && '$$inject' in $$props) {
$$self.$inject_state($$props.$$inject);
}
$$props = exclude_internal_props($$props);
return [Component, __astro_children, props];
}
class App extends SvelteComponentDev {
constructor(options) {
super(options);
init(this, options, instance, create_fragment, not_equal, {});
dispatch_dev('SvelteRegisterComponent', {
component: this,
tagName: 'App',
options,
id: create_fragment.name,
});
}
}
export default App;

View file

@ -1,12 +0,0 @@
/* eslint-disable */
// @ts-nocheck
// TODO: don't precompile this, but it works for now
/* App.svelte generated by Svelte v3.37.0 */
import { create_ssr_component, validate_component } from 'svelte/internal';
const App = create_ssr_component(($$result, $$props, $$bindings, slots) => {
const { __astro_component: Component, __astro_children, ...props } = $$props;
return `${validate_component(Component, 'Component').$$render($$result, Object.assign(props), {}, { default: () => `${__astro_children}` })}`;
});
export default App;

View file

@ -1,11 +1,12 @@
import type { ComponentRenderer } from '../../@types/renderer';
import type { SvelteComponent } from 'svelte';
import { createRenderer } from './renderer';
import SvelteWrapper from '../SvelteWrapper.svelte.server';
import SvelteWrapper from '../SvelteWrapper.server.svelte';
const SvelteRenderer: ComponentRenderer<SvelteComponent> = {
renderStatic(Component) {
return async (props, ...children) => {
/// @ts-expect-error
const { html } = SvelteWrapper.render({ __astro_component: Component, __astro_children: children.join('\n'), ...props });
return html;
};

View file

@ -1,4 +1,4 @@
import SvelteWrapper from '../SvelteWrapper.svelte.client';
import SvelteWrapper from '../SvelteWrapper.client.svelte';
import type { SvelteComponent } from 'svelte';
export default (target: Element, component: SvelteComponent, props: any, children: string) => {

View file

@ -1,3 +1,4 @@
import 'source-map-support/register.js';
import type { CompileError } from 'astro-parser';
import { bold, blue, red, grey, underline } from 'kleur/colors';
import { Writable } from 'stream';

View file

@ -1,3 +1,4 @@
import 'source-map-support/register.js';
import { fileURLToPath } from 'url';
import type { SnowpackDevServer, ServerRuntime as SnowpackServerRuntime, SnowpackConfig } from 'snowpack';
import type { AstroConfig, CollectionResult, CollectionRSS, CreateCollection, Params, RuntimeMode } from './@types/astro';

View file

@ -1,3 +1,4 @@
import 'source-map-support/register.js';
import { existsSync } from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';

View file

@ -1,8 +1,9 @@
import { fileURLToPath } from 'url';
import { suite } from 'uvu';
import * as assert from 'uvu/assert';
import { loadConfig } from '../dist/config.js';
import { createRuntime } from '../dist/runtime.js';
import { loadConfig } from '#astro/config';
import { createRuntime } from '#astro/runtime';
const DType = suite('doctype');

View file

@ -1,6 +1,5 @@
import { suite } from 'uvu';
import * as assert from 'uvu/assert';
import { doc } from './test-utils.js';
import { setup, setupBuild } from './helpers.js';
const DynamicComponents = suite('Dynamic components tests');

View file

@ -3,9 +3,9 @@ import { join } from 'path';
import { fileURLToPath } from 'url';
import { suite } from 'uvu';
import * as assert from 'uvu/assert';
import { createRuntime } from '../dist/runtime.js';
import { build } from '../dist/build.js';
import { loadConfig } from '../dist/config.js';
import { createRuntime } from '#astro/runtime';
import { build } from '#astro/build';
import { loadConfig } from '#astro/config';
import { doc } from './test-utils.js';
const { rmdir, readFile } = fsPromises;

View file

@ -1,6 +1,6 @@
import { suite } from 'uvu';
import * as assert from 'uvu/assert';
import { scopeRule } from '../dist/compiler/transform/postcss-scoped-styles/index.js';
import { scopeRule } from '#astro/compiler';
const ScopedStyles = suite('Astro PostCSS Scoped Styles Plugin');

View file

@ -1,7 +1,7 @@
import { suite } from 'uvu';
import * as assert from 'uvu/assert';
import { runDevServer } from './helpers.js';
import { loadConfig } from '../dist/config.js';
import { loadConfig } from '#astro/config';
const ConfigPort = suite('Config path');

View file

@ -1,8 +1,8 @@
import { fileURLToPath } from 'url';
import { build as astroBuild } from '../dist/build.js';
import { build as astroBuild } from '#astro/build';
import { readFile } from 'fs/promises';
import { createRuntime } from '../dist/runtime.js';
import { loadConfig } from '../dist/config.js';
import { createRuntime } from '#astro/runtime';
import { loadConfig } from '#astro/config';
import * as assert from 'uvu/assert';
import execa from 'execa';

View file

@ -1,8 +1,8 @@
import { fileURLToPath } from 'url';
import { suite } from 'uvu';
import * as assert from 'uvu/assert';
import { createRuntime } from '../dist/runtime.js';
import { loadConfig } from '../dist/config.js';
import { createRuntime } from '#astro/runtime';
import { loadConfig } from '#astro/config';
import { doc } from './test-utils.js';
const React = suite('React Components');

View file

@ -1,8 +1,8 @@
import { fileURLToPath } from 'url';
import { suite } from 'uvu';
import * as assert from 'uvu/assert';
import { createRuntime } from '../dist/runtime.js';
import { loadConfig } from '../dist/config.js';
import { createRuntime } from '#astro/runtime';
import { loadConfig } from '#astro/config';
import { promises as fsPromises } from 'fs';
import { relative as pathRelative } from 'path';

View file

@ -1,9 +1,11 @@
{
"extends": "../../tsconfig.base.json",
"include": ["src"],
"include": ["src", "index.d.ts"],
"compilerOptions": {
"allowJs": true,
"target": "ES2020",
"module": "ES2020",
"outDir": "./dist"
"outDir": "./dist",
"declarationDir": "./dist/types"
}
}

View file

@ -1,25 +1,30 @@
import esbuild from 'esbuild';
import svelte from '../utils/svelte-plugin.js';
import del from 'del';
import { promises as fs } from 'fs';
import { dim, green, red, yellow } from 'kleur/colors';
import glob from 'tiny-glob';
/** @type {import('esbuild').BuildOptions} */
const config = {
const defaultConfig = {
bundle: true,
minify: true,
sourcemap: 'inline',
format: 'esm',
platform: 'node',
target: 'node14',
sourcemap: 'inline',
sourcesContent: false,
plugins: [svelte()],
};
export default async function build(pattern, ...args) {
const isDev = args.pop() === 'IS_DEV';
const entryPoints = await glob(pattern, { filesOnly: true, absolute: true });
export default async function build(...args) {
const config = Object.assign({}, defaultConfig);
const isDev = args.slice(-1)[0] === 'IS_DEV';
let entryPoints = [].concat(...(await Promise.all(args.map((pattern) => glob(pattern, { filesOnly: true, absolute: true })))));
const { type = 'module', dependencies = {} } = await fs.readFile('./package.json').then((res) => JSON.parse(res.toString()));
const format = type === 'module' ? 'esm' : 'cjs';
const external = Object.keys(dependencies);
const external = [...Object.keys(dependencies), 'source-map-support', 'source-map-support/register.js'];
const outdir = 'dist';
await clean(outdir);
@ -61,5 +66,5 @@ export default async function build(pattern, ...args) {
}
async function clean(outdir) {
return del(`!${outdir}/**/*.d.ts`);
return del([`${outdir}/**`, `!${outdir}/**/*.d.ts`]);
}

View file

@ -0,0 +1,60 @@
// @ts-nocheck
import { compile } from 'svelte/compiler';
import { relative, isAbsolute, join, dirname } from 'path';
import { promises as fs } from 'fs';
const convertMessage = ({ message, start, end, filename, frame }) => ({
text: message,
location: start && end && {
file: filename,
line: start.line,
column: start.column,
length: start.line === end.line ? end.column - start.column : 0,
lineText: frame,
},
})
const handleLoad = async (args, generate) => {
const { path } = args;
const source = await fs.readFile(path, 'utf8');
const filename = relative(process.cwd(), path)
try {
let compileOptions = { css: false, generate, hydratable: true };
let { js, warnings } = compile(source, { ...compileOptions, filename })
let contents = js.code + `\n//# sourceMappingURL=` + js.map.toUrl()
return { loader: 'js', contents, resolveDir: dirname(path), warnings: warnings.map(w => convertMessage(w)) };
} catch (e) {
return { errors: [convertMessage(e)] }
}
}
export default function sveltePlugin() {
return {
name: 'svelte-esbuild',
setup(build) {
build.onResolve({ filter: /\.svelte$/ }, args => {
let path = args.path.replace(/\.(?:client|server)/, '');
path = isAbsolute(path) ? path : join(args.resolveDir, path)
if (/\.client\.svelte$/.test(args.path)) {
return {
path,
namespace: 'svelte:client',
}
}
if (/\.server\.svelte$/.test(args.path)) {
return {
path,
namespace: 'svelte:server',
}
}
});
build.onLoad({ filter: /.*/, namespace: 'svelte:client' }, (args) => handleLoad(args, 'dom'))
build.onLoad({ filter: /.*/, namespace: 'svelte:server' }, (args) => handleLoad(args, 'ssr'))
},
}
}

View file

@ -4243,6 +4243,11 @@ esbuild@^0.11.16:
resolved "https://registry.npmjs.org/esbuild/-/esbuild-0.11.16.tgz"
integrity sha512-34ZWjo4ouvM5cDe7uRoM9GFuMyEmpH9fnVYmomvS1cq9ED9d/0ZG1r+p4P2VbX7ihjv36zA2SWTmP8Zmt/EANA==
esbuild@^0.11.17:
version "0.11.17"
resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.11.17.tgz#d2a929a918864ab3642178f4c0dc84784f863b53"
integrity sha512-Yj+rQATpLLU36ymg0/laLXn9FUoiGUl7mDPUJ0toQ5nXKFrj/rmIEkQ34T1GeiV2cP2SrT0G0s7xiNuDcsIyBg==
esbuild@^0.9.3:
version "0.9.7"
resolved "https://registry.npmjs.org/esbuild/-/esbuild-0.9.7.tgz"
@ -10799,7 +10804,7 @@ svelte-preprocess@^4.6.0:
detect-indent "^6.0.0"
strip-indent "^3.0.0"
svelte@^3.35.0:
svelte@^3.35.0, svelte@^3.38.0:
version "3.38.0"
resolved "https://registry.npmjs.org/svelte/-/svelte-3.38.0.tgz"
integrity sha512-V0CbyzvXEka7zQtRYt++cpPh0zxxDUTIeNlq4KncGXn2qHnQFZ2i4L4+2QOfLwOudb9cewJeegupBQcscnBaaA==