diff --git a/kastro/compiler/compiler.js b/kastro/compiler/compiler.js index 483c25a..5c92376 100644 --- a/kastro/compiler/compiler.js +++ b/kastro/compiler/compiler.js @@ -182,13 +182,14 @@ const buildTarget = (targetName, props) => { * and returns the bundle name of the asset. * * @const {TargetFunction} */ -const bundleTarget = (targetName, props) => props.BuildMode == BuildMode.Dev +const bundleTarget = (targetName, props) => (props.BuildMode == BuildMode.Dev && !props.alwaysBuild) ? Promise.resolve(props.childTargets[0].slice(1)) : buildTarget(targetName, props).then(({ contentHash }) => { /** @const {string} */ const targetFile = targetName.slice(1); /** @const {string} */ - const bundleName = "build/crate/" + (props.bundleName || `${hash.toStr(contentHash)}.${getExt(targetName)}`); + const bundleName = "build/crate/" + + (props.bundleName || `${hash.toStr(contentHash)}.${props.bundleExt || getExt(targetName)}`); /** @const {!Promise} */ const bundle = mkdir("build/crate", { recursive: true }).then(() => CompressedMimes[getExt(targetName)] diff --git a/kastro/compiler/image.js b/kastro/compiler/image.js index 1f96e47..aeaecff 100644 --- a/kastro/compiler/image.js +++ b/kastro/compiler/image.js @@ -42,9 +42,23 @@ const svgTarget = (_, props) => const inlineSvgTarget = (_, props) => props.childTargets[0].then(({ content }) => optimize(content, SvgoInlineConfig).data); +/** @const {TargetFunction} */ +const svgJsxTarget = (_, props) => props.childTargets[0] + .then(({ targetName: childTargetName }) => import(childTargetName)) + .then((mod) => mod.default(props)) + .then((content) => content); + +/** @const {TargetFunction} */ +const inlineSvgJsxTarget = (_, props) => props.childTargets[0] + .then(({ targetName: childTargetName }) => import(childTargetName)) + .then((mod) => mod.default(props)) + .then((content) => optimize(content, SvgoInlineConfig).data); + export { inlineSvgTarget, + inlineSvgJsxTarget, pngTarget, svgTarget, + svgJsxTarget, webpTarget }; diff --git a/kastro/compiler/loader/workerLoader.js b/kastro/compiler/loader/workerLoader.js deleted file mode 100644 index e69de29..0000000 diff --git a/kastro/compiler/targetRegistry.js b/kastro/compiler/targetRegistry.js index 7cd13b9..5c3c9f4 100644 --- a/kastro/compiler/targetRegistry.js +++ b/kastro/compiler/targetRegistry.js @@ -19,12 +19,18 @@ const TargetFunction = {}; const TARGET_FUNCTIONS = {}; /** - * @param {string} targetName + * @param {string} targetName * @return {TargetFunction} */ -const getTargetFunction = (targetName) => { +const getTargetFunction = (targetName) => TARGET_FUNCTIONS[getLongExt(targetName)]; + +/** + * @param {string} targetName + * @return {string} + */ +const getLongExt = (targetName) => { const nameIdx = targetName.lastIndexOf("/"); - return TARGET_FUNCTIONS[targetName.slice(targetName.indexOf(".", nameIdx))] + return targetName.slice(targetName.indexOf(".", nameIdx)); } /** @@ -38,6 +44,7 @@ const registerTargetFunction = (extension, func) => { } export { + getLongExt, getTargetFunction, Props, registerTargetFunction, diff --git a/kastro/image.js b/kastro/image.js index c9c29eb..1e79286 100644 --- a/kastro/image.js +++ b/kastro/image.js @@ -1,8 +1,8 @@ import { SAXParser } from "sax"; import { tagYaz } from "../util/html"; -import { getExt } from "../util/paths"; +import { getDir, getExt } from "../util/paths"; import compiler from "./compiler/compiler"; -import { Props } from "./compiler/targetRegistry"; +import { Props, getLongExt } from "./compiler/targetRegistry"; import { removeGlobalProps } from "./props"; /** @@ -12,8 +12,9 @@ import { removeGlobalProps } from "./props"; * @returns {!Promise} */ const InlineSvgImage = ({ src, ...props }) => - compiler.buildTarget(`/build/${src.slice(0, -4)}.inl.svg`, { + compiler.buildTarget(`/build/${getDir(src)}.inl${getLongExt(src)}`, { childTargets: [`/${src}`], + alwaysBuild: src.endsWith(".svg.jsx"), ...props }).then(({ content }) => { removeGlobalProps(props); @@ -35,17 +36,21 @@ const InlineSvgImage = ({ src, ...props }) => return result; }); -const makeImageElement = (bundledName, props) => { +const makeImageElement = (bundleName, { inSvg, ...props }) => { removeGlobalProps(props); - props.src = bundledName; - return tagYaz("img", props, true); + if ("piggyback" in props) delete props.piggyback; + if (inSvg) props.href = bundleName; + else props.src = bundleName; + return tagYaz(inSvg ? "image" : "img", props, true); } const SvgImage = ({ src, inline, BuildMode, ...props }) => { if (inline) return InlineSvgImage({ src, ...props }); const suffix = props.width ? "-w" + props.width : ""; - return compiler.bundleTarget(`/build/${src.slice(0, -4)}${suffix}.svg`, { + return compiler.bundleTarget(`/build/${getDir(src)}${suffix}${getLongExt(src)}`, { BuildMode, + alwaysBuild: src.endsWith(".svg.jsx"), + bundleExt: "svg", childTargets: [`/${src}`] }).then((bundledName) => makeImageElement(bundledName, props)); } @@ -82,14 +87,15 @@ const Favicon = ({ src, raster, BuildMode, ...props }) => { */ const Image = ({ inline, ...props }) => { if (inline) { - if (!props.src.endsWith(".svg")) - throw new Error("We only inline svgs; for other formats serving directly is more efficient"); - return InlineSvgImage(props) + if (props.src.endsWith(".svg") || props.src.endsWith(".svg.jsx")) + return InlineSvgImage(props); + throw new Error("We only inline svgs; for other formats serving directly is more efficient"); } if (props.rel == "icon") return Favicon(props); return { + "jsx": SvgImage, "svg": SvgImage, "png": PngImage, }[getExt(props.src)](props); diff --git a/kastro/kastro.js b/kastro/kastro.js index b88c026..2c578ba 100644 --- a/kastro/kastro.js +++ b/kastro/kastro.js @@ -5,7 +5,14 @@ import { parseArgs } from "../util/cli"; import { combine, getDir } from "../util/paths"; import compiler from "./compiler/compiler"; import { ttfTarget, woff2Target } from "./compiler/font"; -import { inlineSvgTarget, pngTarget, svgTarget, webpTarget } from "./compiler/image"; +import { + inlineSvgJsxTarget, + inlineSvgTarget, + pngTarget, + svgJsxTarget, + svgTarget, + webpTarget +} from "./compiler/image"; import { pageTarget } from "./compiler/page"; import { getGlobals } from "./compiler/pageGlobals"; import { scriptTarget } from "./compiler/script"; @@ -15,9 +22,10 @@ import { registerTargetFunction } from "./compiler/targetRegistry"; const setupKastro = () => { registerTargetFunction(".html", pageTarget); registerTargetFunction(".inl.svg", inlineSvgTarget); + registerTargetFunction(".inl.svg.jsx", inlineSvgJsxTarget); registerTargetFunction(".png", pngTarget); registerTargetFunction(".svg", svgTarget); - registerTargetFunction(".m.svg", svgTarget); + registerTargetFunction(".svg.jsx", svgJsxTarget); registerTargetFunction(".css", stylesheetTarget); registerTargetFunction(".webp", webpTarget); registerTargetFunction(".ttf", ttfTarget); @@ -32,14 +40,16 @@ const setupKastro = () => { const stylesheetLoader = await readFile("/" + combine(moduleDir, "./compiler/loader/stylesheetLoader.js"), "utf8"); const scriptLoader = await readFile("/" + combine(moduleDir, "./compiler/loader/scriptLoader.js"), "utf8"); - build.onLoad({ filter: /\.(svg|png|webp)$/ }, (args) => { + const imageComponent = (args) => { const code = `import { Image } from "@kimlikdao/lib/kastro/image";\n` + `export default (props) => Image({...props, src: "${args.path.slice(cwdLen)}" });`; return { contents: code, loader: "js" }; - }); + } + + build.onLoad({ filter: /\.(svg|png|webp)$/ }, imageComponent); build.onLoad({ filter: /\.css$/ }, (args) => ({ contents: stylesheetLoader.replace("SOURCE", args.path.slice(cwdLen)), loader: "js" @@ -52,14 +62,15 @@ const setupKastro = () => { loader: "js" }; }); - build.onResolve({ filter: /./, namespace: "script" }, ({ path, importer }) => ({ - path: path.startsWith(".") ? "/" + combine(getDir(importer.replace("script:", "")), path) : path, - namespace: "script" + build.onResolve({ filter: /./, namespace: "kastro" }, ({ path, importer }) => ({ + path: path.startsWith(".") ? "/" + combine(getDir(importer.replace("kastro:", "")), path) : path, + namespace: "kastro" })); - build.onLoad({ filter: /.*/, namespace: "script" }, (args) => ({ + build.onLoad({ filter: /.js$/, namespace: "kastro" }, (args) => ({ contents: scriptLoader.replace("SOURCE", args.path.slice(cwdLen)), loader: "js" })); + build.onLoad({ filter: /.svg.jsx$/, namespace: "kastro" }, imageComponent); }, });