mirror of
https://github.com/withastro/astro.git
synced 2024-12-16 21:46:22 -05:00
Inline hydration directive scripts (#3605)
* Inline hydration scripts * Adds a changeset * Update directiveAstroKeys type
This commit is contained in:
parent
816e963509
commit
4916b733c2
19 changed files with 203 additions and 174 deletions
5
.changeset/many-seas-notice.md
Normal file
5
.changeset/many-seas-notice.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
'astro': patch
|
||||
---
|
||||
|
||||
Inlines hydration scripts
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -16,6 +16,7 @@ package-lock.json
|
|||
# do not commit .env files or any files that end with `.env`
|
||||
*.env
|
||||
|
||||
packages/astro/src/**/*.prebuilt.ts
|
||||
!packages/astro/vendor/vite/dist
|
||||
packages/integrations/**/.netlify/
|
||||
|
||||
|
|
|
@ -66,10 +66,10 @@
|
|||
"vendor"
|
||||
],
|
||||
"scripts": {
|
||||
"prebuild": "astro-scripts prebuild --to-string \"src/runtime/server/astro-island.ts\"",
|
||||
"build": "astro-scripts build \"src/**/*.ts\" && tsc",
|
||||
"build:ci": "astro-scripts build \"src/**/*.ts\"",
|
||||
"dev": "astro-scripts dev \"src/**/*.ts\"",
|
||||
"prebuild": "astro-scripts prebuild --to-string \"src/runtime/server/astro-island.ts\" \"src/runtime/client/{idle,load,media,only,visible}.ts\"",
|
||||
"build": "pnpm run prebuild && astro-scripts build \"src/**/*.ts\" && tsc",
|
||||
"build:ci": "pnpm run prebuild && astro-scripts build \"src/**/*.ts\"",
|
||||
"dev": "astro-scripts dev --prebuild \"src/runtime/server/astro-island.ts\" --prebuild \"src/runtime/client/{idle,load,media,only,visible}.ts\" \"src/**/*.ts\"",
|
||||
"postbuild": "astro-scripts copy \"src/**/*.astro\"",
|
||||
"benchmark": "node test/benchmark/dev.bench.js && node test/benchmark/build.bench.js",
|
||||
"test": "mocha --exit --timeout 20000 --ignore **/lit-element.test.js --ignore **/errors.test.js && mocha --timeout 20000 **/lit-element.test.js && mocha --timeout 20000 **/errors.test.js",
|
||||
|
|
|
@ -68,8 +68,6 @@ export async function staticBuild(opts: StaticBuildOptions) {
|
|||
...metadata.hydratedComponentPaths(),
|
||||
// Client-only components
|
||||
...clientOnlys,
|
||||
// Any hydration directive like astro/client/idle.js
|
||||
...metadata.hydrationDirectiveSpecifiers(),
|
||||
// The client path for each renderer
|
||||
...renderers
|
||||
.filter((renderer) => !!renderer.clientEntrypoint)
|
||||
|
|
17
packages/astro/src/runtime/client/hydration-directives.d.ts
vendored
Normal file
17
packages/astro/src/runtime/client/hydration-directives.d.ts
vendored
Normal file
|
@ -0,0 +1,17 @@
|
|||
import type { GetHydrateCallback, HydrateOptions } from '../../@types/astro';
|
||||
|
||||
type DirectiveLoader = (get: GetHydrateCallback, opts: HydrateOptions, root: HTMLElement) => void;
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
Astro: {
|
||||
idle: DirectiveLoader;
|
||||
load: DirectiveLoader;
|
||||
media: DirectiveLoader;
|
||||
only: DirectiveLoader;
|
||||
visible: DirectiveLoader;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export {};
|
|
@ -1,27 +1,12 @@
|
|||
import type { GetHydrateCallback, HydrateOptions } from '../../@types/astro';
|
||||
import { notify } from './events';
|
||||
(self.Astro = self.Astro || {}).idle = (getHydrateCallback) => {
|
||||
const cb = async () => {
|
||||
let hydrate = await getHydrateCallback();
|
||||
await hydrate();
|
||||
};
|
||||
|
||||
/**
|
||||
* Hydrate this component as soon as the main thread is free
|
||||
* (or after a short delay, if `requestIdleCallback`) isn't supported
|
||||
*/
|
||||
export default async function onIdle(
|
||||
root: HTMLElement,
|
||||
options: HydrateOptions,
|
||||
getHydrateCallback: GetHydrateCallback
|
||||
) {
|
||||
async function idle() {
|
||||
const cb = async () => {
|
||||
let hydrate = await getHydrateCallback();
|
||||
await hydrate();
|
||||
notify();
|
||||
};
|
||||
|
||||
if ('requestIdleCallback' in window) {
|
||||
(window as any).requestIdleCallback(cb);
|
||||
} else {
|
||||
setTimeout(cb, 200);
|
||||
}
|
||||
if ('requestIdleCallback' in window) {
|
||||
(window as any).requestIdleCallback(cb);
|
||||
} else {
|
||||
setTimeout(cb, 200);
|
||||
}
|
||||
idle();
|
||||
}
|
||||
|
|
|
@ -1,18 +1,8 @@
|
|||
import type { GetHydrateCallback, HydrateOptions } from '../../@types/astro';
|
||||
import { notify } from './events';
|
||||
|
||||
/**
|
||||
* Hydrate this component immediately
|
||||
*/
|
||||
export default async function onLoad(
|
||||
root: HTMLElement,
|
||||
options: HydrateOptions,
|
||||
getHydrateCallback: GetHydrateCallback
|
||||
) {
|
||||
async function load() {
|
||||
(self.Astro = self.Astro || {}).load = (getHydrateCallback) => {
|
||||
(async () => {
|
||||
let hydrate = await getHydrateCallback();
|
||||
await hydrate();
|
||||
notify();
|
||||
}
|
||||
load();
|
||||
}
|
||||
})();
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -1,29 +1,22 @@
|
|||
import type { GetHydrateCallback, HydrateOptions } from '../../@types/astro';
|
||||
import { notify } from './events';
|
||||
|
||||
/**
|
||||
* Hydrate this component when a matching media query is found
|
||||
*/
|
||||
export default async function onMedia(
|
||||
root: HTMLElement,
|
||||
options: HydrateOptions,
|
||||
getHydrateCallback: GetHydrateCallback
|
||||
) {
|
||||
async function media() {
|
||||
const cb = async () => {
|
||||
let hydrate = await getHydrateCallback();
|
||||
await hydrate();
|
||||
notify();
|
||||
};
|
||||
(self.Astro = self.Astro || {}).media = (getHydrateCallback, options) => {
|
||||
const cb = async () => {
|
||||
let hydrate = await getHydrateCallback();
|
||||
await hydrate();
|
||||
};
|
||||
|
||||
if (options.value) {
|
||||
const mql = matchMedia(options.value);
|
||||
if (mql.matches) {
|
||||
cb();
|
||||
} else {
|
||||
mql.addEventListener('change', cb, { once: true });
|
||||
}
|
||||
if (options.value) {
|
||||
const mql = matchMedia(options.value);
|
||||
if (mql.matches) {
|
||||
cb();
|
||||
} else {
|
||||
mql.addEventListener('change', cb, { once: true });
|
||||
}
|
||||
}
|
||||
media();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -1,18 +1,12 @@
|
|||
import type { GetHydrateCallback, HydrateOptions } from '../../@types/astro';
|
||||
import { notify } from './events';
|
||||
|
||||
/**
|
||||
* Hydrate this component only on the client
|
||||
*/
|
||||
export default async function onOnly(
|
||||
root: HTMLElement,
|
||||
options: HydrateOptions,
|
||||
getHydrateCallback: GetHydrateCallback
|
||||
) {
|
||||
async function only() {
|
||||
(self.Astro = self.Astro || {}).only = (getHydrateCallback) => {
|
||||
(async () => {
|
||||
let hydrate = await getHydrateCallback();
|
||||
await hydrate();
|
||||
notify();
|
||||
}
|
||||
only();
|
||||
}
|
||||
})();
|
||||
};
|
||||
|
||||
|
||||
|
|
@ -1,30 +1,15 @@
|
|||
import type { GetHydrateCallback, HydrateOptions } from '../../@types/astro';
|
||||
import { notify } from './events';
|
||||
|
||||
/**
|
||||
* Hydrate this component when one of it's children becomes visible
|
||||
* We target the children because `astro-island` is set to `display: contents`
|
||||
* which doesn't work with IntersectionObserver
|
||||
*/
|
||||
export default async function onVisible(
|
||||
root: HTMLElement,
|
||||
options: HydrateOptions,
|
||||
getHydrateCallback: GetHydrateCallback
|
||||
) {
|
||||
let io: IntersectionObserver;
|
||||
|
||||
async function visible() {
|
||||
const cb = async () => {
|
||||
(self.Astro = self.Astro || {}).visible = (getHydrateCallback, _opts, root) => {
|
||||
const cb = async () => {
|
||||
let hydrate = await getHydrateCallback();
|
||||
await hydrate();
|
||||
notify();
|
||||
};
|
||||
|
||||
if (io) {
|
||||
io.disconnect();
|
||||
}
|
||||
|
||||
io = new IntersectionObserver((entries) => {
|
||||
let io = new IntersectionObserver((entries) => {
|
||||
for (const entry of entries) {
|
||||
if (!entry.isIntersecting) continue;
|
||||
// As soon as we hydrate, disconnect this IntersectionObserver for every `astro-island`
|
||||
|
@ -38,7 +23,4 @@ export default async function onVisible(
|
|||
const child = root.children[i];
|
||||
io.observe(child);
|
||||
}
|
||||
}
|
||||
|
||||
visible();
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
/**
|
||||
* This file is prebuilt from packages/astro/src/runtime/server/astro-island.ts
|
||||
* Do not edit this directly, but instead edit that file and rerun the prebuild
|
||||
* to generate this file.
|
||||
*/
|
||||
|
||||
export default `var a;{const o={0:t=>t,1:t=>JSON.parse(t,n),2:t=>new RegExp(t),3:t=>new Date(t),4:t=>new Map(JSON.parse(t,n)),5:t=>new Set(JSON.parse(t,n)),6:t=>BigInt(t),7:t=>new URL(t)},n=(t,e)=>{if(t===""||!Array.isArray(e))return e;const[r,s]=e;return r in o?o[r](s):void 0};customElements.get("astro-island")||customElements.define("astro-island",(a=class extends HTMLElement{constructor(){super(...arguments);this.hydrate=()=>{if(!this.hydrator||this.parentElement?.closest("astro-island[ssr]"))return;let e=null,r=this.querySelector("astro-fragment");if(r==null&&this.hasAttribute("tmpl")){let i=this.querySelector("template[data-astro-template]");i&&(e=i.innerHTML,i.remove())}else r&&(e=r.innerHTML);const s=this.hasAttribute("props")?JSON.parse(this.getAttribute("props"),n):{};this.hydrator(this)(this.Component,s,e,{client:this.getAttribute("client")}),this.removeAttribute("ssr"),window.removeEventListener("astro:hydrate",this.hydrate),window.dispatchEvent(new CustomEvent("astro:hydrate"))}}async connectedCallback(){const[{default:e}]=await Promise.all([import(this.getAttribute("directive-url")),import(this.getAttribute("before-hydration-url"))]);window.addEventListener("astro:hydrate",this.hydrate);const r=JSON.parse(this.getAttribute("opts"));e(this,r,async()=>{const s=this.getAttribute("renderer-url"),[i,{default:l}]=await Promise.all([import(this.getAttribute("component-url")),s?import(s):()=>()=>{}]);return this.Component=i[this.getAttribute("component-export")||"default"],this.hydrator=l,this.hydrate})}attributeChangedCallback(){this.hydrator&&this.hydrate()}},a.observedAttributes=["props"],a))}`;
|
|
@ -2,6 +2,16 @@
|
|||
// Do not import this file directly, instead import the prebuilt one instead.
|
||||
// pnpm --filter astro run prebuild
|
||||
|
||||
type directiveAstroKeys = 'load' | 'idle' | 'visible' | 'media' | 'only';
|
||||
|
||||
declare const Astro: {
|
||||
[k in directiveAstroKeys]: (
|
||||
fn: () => Promise<() => void>,
|
||||
opts: Record<string, any>,
|
||||
root: HTMLElement
|
||||
) => void;
|
||||
}
|
||||
|
||||
{
|
||||
interface PropTypeSelector {
|
||||
[k: string]: (value: any) => any;
|
||||
|
@ -32,14 +42,10 @@
|
|||
public hydrator: any;
|
||||
static observedAttributes = ['props'];
|
||||
async connectedCallback() {
|
||||
const [{ default: setup }] = await Promise.all([
|
||||
import(this.getAttribute('directive-url')!),
|
||||
import(this.getAttribute('before-hydration-url')!),
|
||||
]);
|
||||
window.addEventListener('astro:hydrate', this.hydrate);
|
||||
|
||||
const opts = JSON.parse(this.getAttribute('opts')!);
|
||||
setup(this, opts, async () => {
|
||||
await import(this.getAttribute('before-hydration-url')!);
|
||||
const opts = JSON.parse(this.getAttribute('opts')!) as Record<string, any>;
|
||||
Astro[this.getAttribute('client') as directiveAstroKeys](async () => {
|
||||
const rendererUrl = this.getAttribute('renderer-url');
|
||||
const [componentModule, { default: hydrator }] = await Promise.all([
|
||||
import(this.getAttribute('component-url')!),
|
||||
|
@ -48,7 +54,7 @@
|
|||
this.Component = componentModule[this.getAttribute('component-export') || 'default'];
|
||||
this.hydrator = hydrator;
|
||||
return this.hydrate;
|
||||
});
|
||||
}, opts, this);
|
||||
}
|
||||
hydrate = () => {
|
||||
if (!this.hydrator || this.parentElement?.closest('astro-island[ssr]')) {
|
||||
|
|
|
@ -6,7 +6,7 @@ import type {
|
|||
} from '../../@types/astro';
|
||||
import { escapeHTML } from './escape.js';
|
||||
import { serializeProps } from './serialize.js';
|
||||
import { hydrationSpecifier, serializeListValue } from './util.js';
|
||||
import { serializeListValue } from './util.js';
|
||||
|
||||
const HydrationDirectives = ['load', 'idle', 'media', 'visible', 'only'];
|
||||
|
||||
|
@ -129,7 +129,6 @@ export async function generateHydrateScript(
|
|||
|
||||
island.props['ssr'] = '';
|
||||
island.props['client'] = hydrate;
|
||||
island.props['directive-url'] = await result.resolve(hydrationSpecifier(hydrate));
|
||||
island.props['before-hydration-url'] = await result.resolve('astro:scripts/before-hydration.js');
|
||||
island.props['opts'] = escapeHTML(
|
||||
JSON.stringify({
|
||||
|
|
|
@ -8,11 +8,17 @@ import type {
|
|||
SSRLoadedRenderer,
|
||||
SSRResult,
|
||||
} from '../../@types/astro';
|
||||
import islandScript from './astro-island.prebuilt.js';
|
||||
|
||||
import { escapeHTML, HTMLString, markHTMLString } from './escape.js';
|
||||
import { extractDirectives, generateHydrateScript } from './hydration.js';
|
||||
import { serializeProps } from './serialize.js';
|
||||
import { shorthash } from './shorthash.js';
|
||||
import {
|
||||
determineIfNeedsHydrationScript,
|
||||
determinesIfNeedsDirectiveScript,
|
||||
PrescriptType,
|
||||
getPrescripts
|
||||
} from './scripts.js';
|
||||
import { serializeListValue } from './util.js';
|
||||
|
||||
export { markHTMLString, markHTMLString as unescapeHTML } from './escape.js';
|
||||
|
@ -27,18 +33,6 @@ const htmlEnumAttributes = /^(contenteditable|draggable|spellcheck|value)$/i;
|
|||
// Note: SVG is case-sensitive!
|
||||
const svgEnumAttributes = /^(autoReverse|externalResourcesRequired|focusable|preserveAlpha)$/i;
|
||||
|
||||
// This is used to keep track of which requests (pages) have had the hydration script
|
||||
// appended. We only add the hydration script once per page, and since the SSRResult
|
||||
// object corresponds to one page request, we are using it as a key to know.
|
||||
const resultsWithHydrationScript = new WeakSet<SSRResult>();
|
||||
|
||||
function determineIfNeedsHydrationScript(result: SSRResult): boolean {
|
||||
if (resultsWithHydrationScript.has(result)) {
|
||||
return false;
|
||||
}
|
||||
resultsWithHydrationScript.add(result);
|
||||
return true;
|
||||
}
|
||||
|
||||
// INVESTIGATE:
|
||||
// 2. Less anys when possible and make it well known when they are needed.
|
||||
|
@ -158,6 +152,7 @@ function formatList(values: string[]): string {
|
|||
return `${values.slice(0, -1).join(', ')} or ${values[values.length - 1]}`;
|
||||
}
|
||||
|
||||
|
||||
export async function renderComponent(
|
||||
result: SSRResult,
|
||||
displayName: string,
|
||||
|
@ -191,6 +186,7 @@ export async function renderComponent(
|
|||
const { hydration, props } = extractDirectives(_props);
|
||||
let html = '';
|
||||
let needsHydrationScript = hydration && determineIfNeedsHydrationScript(result);
|
||||
let needsDirectiveScript = hydration && determinesIfNeedsDirectiveScript(result, hydration.directive);
|
||||
|
||||
if (hydration) {
|
||||
metadata.hydrate = hydration.directive as AstroComponentMetadata['hydrate'];
|
||||
|
@ -348,19 +344,11 @@ If you're still stuck, please open an issue on GitHub or join us at https://astr
|
|||
|
||||
island.children = `${html ?? ''}${template}`;
|
||||
|
||||
// Add the astro-island definition only once. Since the SSRResult object
|
||||
// is scoped to a page renderer we can use it as a key to know if the script
|
||||
// has been rendered or not.
|
||||
let script = '';
|
||||
if (needsHydrationScript) {
|
||||
// Note that this is a class script, not a module script.
|
||||
// This is so that it executes immediate, and when the browser encounters
|
||||
// an astro-island element the callbacks will fire immediately, causing the JS
|
||||
// deps to be loaded immediately.
|
||||
script = `<script>${islandScript}</script>`;
|
||||
}
|
||||
|
||||
return markHTMLString(script + renderElement('astro-island', island, false));
|
||||
let prescriptType: PrescriptType = needsHydrationScript ? 'both' : needsDirectiveScript ?
|
||||
'directive' : null;
|
||||
let prescripts = getPrescripts(prescriptType, hydration.directive);
|
||||
|
||||
return markHTMLString(prescripts + renderElement('astro-island', island, false));
|
||||
}
|
||||
|
||||
/** Create the Astro.fetchContent() runtime function. */
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
import { hydrationSpecifier } from './util.js';
|
||||
|
||||
interface ModuleInfo {
|
||||
module: Record<string, any>;
|
||||
specifier: string;
|
||||
|
@ -82,21 +80,6 @@ export class Metadata {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all of the hydration specifiers used within this component.
|
||||
*/
|
||||
*hydrationDirectiveSpecifiers() {
|
||||
const found = new Set<string>();
|
||||
for (const metadata of this.deepMetadata()) {
|
||||
for (const directive of metadata.hydrationDirectives) {
|
||||
if (!found.has(directive)) {
|
||||
found.add(directive);
|
||||
yield hydrationSpecifier(directive);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*hoistedScriptPaths() {
|
||||
for (const metadata of this.deepMetadata()) {
|
||||
let i = 0,
|
||||
|
|
80
packages/astro/src/runtime/server/scripts.ts
Normal file
80
packages/astro/src/runtime/server/scripts.ts
Normal file
|
@ -0,0 +1,80 @@
|
|||
import type {
|
||||
APIContext,
|
||||
AstroComponentMetadata,
|
||||
AstroGlobalPartial,
|
||||
EndpointHandler,
|
||||
Params,
|
||||
SSRElement,
|
||||
SSRLoadedRenderer,
|
||||
SSRResult,
|
||||
} from '../../@types/astro';
|
||||
|
||||
import islandScript from './astro-island.prebuilt.js';
|
||||
import idlePrebuilt from '../client/idle.prebuilt.js';
|
||||
import loadPrebuilt from '../client/load.prebuilt.js';
|
||||
import onlyPrebuilt from '../client/only.prebuilt.js';
|
||||
import visiblePrebuilt from '../client/visible.prebuilt.js';
|
||||
import mediaPrebuilt from '../client/media.prebuilt.js';
|
||||
|
||||
|
||||
// This is used to keep track of which requests (pages) have had the hydration script
|
||||
// appended. We only add the hydration script once per page, and since the SSRResult
|
||||
// object corresponds to one page request, we are using it as a key to know.
|
||||
const resultsWithHydrationScript = new WeakSet<SSRResult>();
|
||||
|
||||
export function determineIfNeedsHydrationScript(result: SSRResult): boolean {
|
||||
if (resultsWithHydrationScript.has(result)) {
|
||||
return false;
|
||||
}
|
||||
resultsWithHydrationScript.add(result);
|
||||
return true;
|
||||
}
|
||||
|
||||
export const hydrationScripts: Record<string, string> = {
|
||||
idle: idlePrebuilt,
|
||||
load: loadPrebuilt,
|
||||
only: onlyPrebuilt,
|
||||
media: mediaPrebuilt,
|
||||
visible: visiblePrebuilt
|
||||
};
|
||||
|
||||
const resultsWithDirectiveScript = new Map<string, WeakSet<SSRResult>>();
|
||||
|
||||
export function determinesIfNeedsDirectiveScript(result: SSRResult, directive: string): boolean {
|
||||
if(!resultsWithDirectiveScript.has(directive)) {
|
||||
resultsWithDirectiveScript.set(directive, new WeakSet());
|
||||
}
|
||||
const set = resultsWithDirectiveScript.get(directive)!;
|
||||
if(set.has(result)) {
|
||||
return false;
|
||||
}
|
||||
set.add(result);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
export type PrescriptType = null | 'both' | 'directive';
|
||||
|
||||
function getDirectiveScriptText(directive: string): string {
|
||||
if(!(directive in hydrationScripts)) {
|
||||
throw new Error(`Unknown directive: ${directive}`);
|
||||
}
|
||||
const directiveScriptText = hydrationScripts[directive];
|
||||
return directiveScriptText;
|
||||
}
|
||||
|
||||
export function getPrescripts(type: PrescriptType, directive: string): string {
|
||||
// Note that this is a classic script, not a module script.
|
||||
// This is so that it executes immediate, and when the browser encounters
|
||||
// an astro-island element the callbacks will fire immediately, causing the JS
|
||||
// deps to be loaded immediately.
|
||||
switch(type) {
|
||||
case 'both':
|
||||
|
||||
return `<script>${getDirectiveScriptText(directive) + islandScript}</script>`;
|
||||
case 'directive':
|
||||
return `<script>${getDirectiveScriptText(directive)}</script>`;
|
||||
}
|
||||
return '';
|
||||
}
|
|
@ -34,12 +34,3 @@ export function serializeListValue(value: any) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the import specifier for a given hydration directive.
|
||||
* @param hydrate The hydration directive such as `idle` or `visible`
|
||||
* @returns
|
||||
*/
|
||||
export function hydrationSpecifier(hydrate: string) {
|
||||
return `astro/client/${hydrate}.js`;
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import del from 'del';
|
|||
import { promises as fs } from 'fs';
|
||||
import { dim, green, red, yellow } from 'kleur/colors';
|
||||
import glob from 'tiny-glob';
|
||||
import prebuild from './prebuild.js';
|
||||
|
||||
/** @type {import('esbuild').BuildOptions} */
|
||||
const defaultConfig = {
|
||||
|
@ -20,9 +21,23 @@ const dt = new Intl.DateTimeFormat('en-us', {
|
|||
minute: '2-digit',
|
||||
});
|
||||
|
||||
function getPrebuilds(isDev, args) {
|
||||
let prebuilds = [];
|
||||
while(args.includes('--prebuild')) {
|
||||
let idx = args.indexOf('--prebuild');
|
||||
prebuilds.push(args[idx + 1]);
|
||||
args.splice(idx, 2);
|
||||
}
|
||||
if(prebuilds.length && isDev) {
|
||||
prebuilds.unshift('--no-minify');
|
||||
}
|
||||
return prebuilds;
|
||||
}
|
||||
|
||||
export default async function build(...args) {
|
||||
const config = Object.assign({}, defaultConfig);
|
||||
const isDev = args.slice(-1)[0] === 'IS_DEV';
|
||||
const prebuilds = getPrebuilds(isDev, args);
|
||||
const patterns = args
|
||||
.filter((f) => !!f) // remove empty args
|
||||
.map((f) => f.replace(/^'/, '').replace(/'$/, '')); // Needed for Windows: glob strings contain surrounding string chars??? remove these
|
||||
|
@ -59,6 +74,9 @@ export default async function build(...args) {
|
|||
...config,
|
||||
watch: {
|
||||
onRebuild(error, result) {
|
||||
if(prebuilds.length) {
|
||||
prebuild(...prebuilds);
|
||||
}
|
||||
const date = dt.format(new Date());
|
||||
if (error || (result && result.errors.length)) {
|
||||
console.error(dim(`[${date}] `) + red(error || result.errors.join('\n')));
|
||||
|
|
|
@ -11,6 +11,12 @@ export default async function prebuild(...args) {
|
|||
args.splice(buildToString, 1);
|
||||
buildToString = true;
|
||||
}
|
||||
let minify = true;
|
||||
let minifyIdx = args.indexOf('--no-minify');
|
||||
if(minifyIdx !== -1) {
|
||||
minify = false;
|
||||
args.splice(minifyIdx, 1);
|
||||
}
|
||||
|
||||
let patterns = args;
|
||||
let entryPoints = [].concat(
|
||||
|
@ -33,7 +39,7 @@ export default async function prebuild(...args) {
|
|||
const tscode = await fs.promises.readFile(filepath, 'utf-8');
|
||||
const esbuildresult = await esbuild.transform(tscode, {
|
||||
loader: 'ts',
|
||||
minify: true,
|
||||
minify,
|
||||
});
|
||||
const rootURL = new URL('../../', import.meta.url);
|
||||
const rel = path.relative(fileURLToPath(rootURL), filepath);
|
||||
|
|
Loading…
Reference in a new issue