diff --git a/content/features/upcoming features.md b/content/features/upcoming features.md index d7acd2a7..676bb71e 100644 --- a/content/features/upcoming features.md +++ b/content/features/upcoming features.md @@ -4,6 +4,7 @@ draft: true ## high priority +- local fonts - images in same folder are broken on shortest path mode - https://help.obsidian.md/Editing+and+formatting/Tags#Nested+tags nested tags?? and big tag listing - watch mode for config/source code diff --git a/package-lock.json b/package-lock.json index bd6566fc..7bb72bc2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,6 +25,7 @@ "hast-util-to-string": "^2.0.0", "is-absolute-url": "^4.0.1", "js-yaml": "^4.1.0", + "lightningcss": "^1.21.5", "mdast-util-find-and-replace": "^2.2.2", "mdast-util-to-string": "^3.2.0", "micromorph": "^0.4.5", @@ -2378,6 +2379,17 @@ "node": ">=6" } }, + "node_modules/detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", + "bin": { + "detect-libc": "bin/detect-libc.js" + }, + "engines": { + "node": ">=0.10" + } + }, "node_modules/diff": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/diff/-/diff-5.1.0.tgz", @@ -3384,6 +3396,183 @@ "node": ">=6" } }, + "node_modules/lightningcss": { + "version": "1.21.5", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.21.5.tgz", + "integrity": "sha512-/pEUPeih2EwIx9n4T82aOG6CInN83tl/mWlw6B5gWLf36UplQi1L+5p3FUHsdt4fXVfOkkh9KIaM3owoq7ss8A==", + "dependencies": { + "detect-libc": "^1.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-darwin-arm64": "1.21.5", + "lightningcss-darwin-x64": "1.21.5", + "lightningcss-linux-arm-gnueabihf": "1.21.5", + "lightningcss-linux-arm64-gnu": "1.21.5", + "lightningcss-linux-arm64-musl": "1.21.5", + "lightningcss-linux-x64-gnu": "1.21.5", + "lightningcss-linux-x64-musl": "1.21.5", + "lightningcss-win32-x64-msvc": "1.21.5" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.21.5", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.21.5.tgz", + "integrity": "sha512-z05hyLX85WY0UfhkFUOrWEFqD69lpVAmgl3aDzMKlIZJGygbhbegqb4PV8qfUrKKNBauut/qVNPKZglhTaDDxA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.21.5", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.21.5.tgz", + "integrity": "sha512-MSJhmej/U9MrdPxDk7+FWhO8+UqVoZUHG4VvKT5RQ4RJtqtANTiWiI97LvoVNMtdMnHaKs1Pkji6wHUFxjJsHQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.21.5", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.21.5.tgz", + "integrity": "sha512-xN6+5/JsMrbZHL1lPl+MiNJ3Xza12ueBKPepiyDCFQzlhFRTj7D0LG+cfNTzPBTO8KcYQynLpl1iBB8LGp3Xtw==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.21.5", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.21.5.tgz", + "integrity": "sha512-KfzFNhC4XTbmG3ma/xcTs/IhCwieW89XALIusKmnV0N618ZDXEB0XjWOYQRCXeK9mfqPdbTBpurEHV/XZtkniQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.21.5", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.21.5.tgz", + "integrity": "sha512-bc0GytQO5Mn9QM6szaZ+31fQHNdidgpM1sSCwzPItz8hg3wOvKl8039rU0veMJV3ZgC9z0ypNRceLrSHeRHmXw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.21.5", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.21.5.tgz", + "integrity": "sha512-JwMbgypPQgc2kW2av3OwzZ8cbrEuIiDiXPJdXRE6aVxu67yHauJawQLqJKTGUhiAhy6iLDG8Wg0a3/ziL+m+Kw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.21.5", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.21.5.tgz", + "integrity": "sha512-Ib8b6IQ/OR/VrPU6YBgy4T3QnuHY7DUa95O+nz+cwrTkMSN6fuHcTcIaz4t8TJ6HI5pl3uxUOZjmtls2pyQWow==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.21.5", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.21.5.tgz", + "integrity": "sha512-A8cSi8lUpBeVmoF+DqqW7cd0FemDbCuKr490IXdjyeI+KL8adpSKUs8tcqO0OXPh1EoDqK7JNkD/dELmd4Iz5g==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, "node_modules/longest-streak": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", diff --git a/package.json b/package.json index 8ece9eb8..cfd1d155 100644 --- a/package.json +++ b/package.json @@ -44,6 +44,7 @@ "hast-util-to-string": "^2.0.0", "is-absolute-url": "^4.0.1", "js-yaml": "^4.1.0", + "lightningcss": "^1.21.5", "mdast-util-find-and-replace": "^2.2.2", "mdast-util-to-string": "^3.2.0", "micromorph": "^0.4.5", diff --git a/quartz.config.ts b/quartz.config.ts index 5378a8e5..211fa937 100644 --- a/quartz.config.ts +++ b/quartz.config.ts @@ -95,7 +95,7 @@ const config: QuartzConfig = { filters: [Plugin.RemoveDrafts()], emitters: [ Plugin.AliasRedirects(), - Plugin.ComponentResources(), + Plugin.ComponentResources({ fontOrigin: "googleFonts" }), Plugin.ContentPage({ ...sharedPageComponents, ...contentPageLayout, diff --git a/quartz/bootstrap-cli.mjs b/quartz/bootstrap-cli.mjs index 1f853a2c..bc5df4e0 100755 --- a/quartz/bootstrap-cli.mjs +++ b/quartz/bootstrap-cli.mjs @@ -11,6 +11,7 @@ import { intro, isCancel, outro, select, text } from "@clack/prompts" import { rimraf } from "rimraf" import prettyBytes from "pretty-bytes" import { spawnSync } from "child_process" +import { transform } from "lightningcss" const UPSTREAM_NAME = "upstream" const QUARTZ_SOURCE_BRANCH = "v4-alpha" @@ -302,6 +303,7 @@ See the [documentation](https://quartz.jzhao.xyz) for how to get started. plugins: [ sassPlugin({ type: "css-text", + cssImports: true, }), { name: "inline-script-loader", diff --git a/quartz/plugins/emitters/componentResources.ts b/quartz/plugins/emitters/componentResources.ts index bc1d4ab4..72a8841d 100644 --- a/quartz/plugins/emitters/componentResources.ts +++ b/quartz/plugins/emitters/componentResources.ts @@ -1,5 +1,5 @@ import { FilePath, ServerSlug } from "../../path" -import { PluginTypes, QuartzEmitterPlugin } from "../types" +import { QuartzEmitterPlugin } from "../types" // @ts-ignore import spaRouterScript from "../../components/scripts/spa.inline" @@ -13,6 +13,7 @@ import { BuildCtx } from "../../ctx" import { StaticResources } from "../../resources" import { QuartzComponent } from "../../components/types" import { googleFontHref, joinStyles } from "../../theme" +import { transform } from "lightningcss" type ComponentResources = { css: string[] @@ -67,7 +68,6 @@ function addGlobalPageResources( ) { const cfg = ctx.cfg.configuration const reloadScript = ctx.argv.serve - staticResources.css.push(googleFontHref(cfg.theme)) // popovers if (cfg.enablePopovers) { @@ -120,35 +120,61 @@ function addGlobalPageResources( } } -export const ComponentResources: QuartzEmitterPlugin = () => ({ - name: "ComponentResources", - getQuartzComponents() { - return [] - }, - async emit(ctx, _content, resources, emit): Promise { - // component specific scripts and styles - const componentResources = getComponentResources(ctx) - // important that this goes *after* component scripts - // as the "nav" event gets triggered here and we should make sure - // that everyone else had the chance to register a listener for it - addGlobalPageResources(ctx, resources, componentResources) - const fps = await Promise.all([ - emit({ - slug: "index" as ServerSlug, - ext: ".css", - content: joinStyles(ctx.cfg.configuration.theme, styles, ...componentResources.css), - }), - emit({ - slug: "prescript" as ServerSlug, - ext: ".js", - content: joinScripts(componentResources.beforeDOMLoaded), - }), - emit({ - slug: "postscript" as ServerSlug, - ext: ".js", - content: joinScripts(componentResources.afterDOMLoaded), - }), - ]) - return fps - }, -}) +interface Options { + fontOrigin: "googleFonts" | "local" +} + +const defaultOptions: Options = { + fontOrigin: "googleFonts", +} + +export const ComponentResources: QuartzEmitterPlugin = (opts?: Partial) => { + const { fontOrigin } = { ...defaultOptions, ...opts } + return { + name: "ComponentResources", + getQuartzComponents() { + return [] + }, + async emit(ctx, _content, resources, emit): Promise { + // component specific scripts and styles + const componentResources = getComponentResources(ctx) + // important that this goes *after* component scripts + // as the "nav" event gets triggered here and we should make sure + // that everyone else had the chance to register a listener for it + + if (fontOrigin === "googleFonts") { + resources.css.push(googleFontHref(ctx.cfg.configuration.theme)) + } else if (fontOrigin === "local") { + // let the user do it themselves in css + } + + addGlobalPageResources(ctx, resources, componentResources) + + const stylesheet = joinStyles(ctx.cfg.configuration.theme, styles, ...componentResources.css) + const prescript = joinScripts(componentResources.beforeDOMLoaded) + const postscript = joinScripts(componentResources.afterDOMLoaded) + const fps = await Promise.all([ + emit({ + slug: "index" as ServerSlug, + ext: ".css", + content: transform({ + filename: "index.css", + code: Buffer.from(stylesheet), + minify: true + }).code.toString(), + }), + emit({ + slug: "prescript" as ServerSlug, + ext: ".js", + content: prescript, + }), + emit({ + slug: "postscript" as ServerSlug, + ext: ".js", + content: postscript, + }), + ]) + return fps + }, + } +} diff --git a/quartz/styles/base.scss b/quartz/styles/base.scss index ee39b47d..fd94a1a1 100644 --- a/quartz/styles/base.scss +++ b/quartz/styles/base.scss @@ -1,6 +1,6 @@ +@use "./custom.scss"; @use "./syntax.scss"; @use "./callouts.scss"; -@use "./custom.scss"; @use "./variables.scss" as *; html { diff --git a/quartz/theme.ts b/quartz/theme.ts index 2860e2c1..b01bfdca 100644 --- a/quartz/theme.ts +++ b/quartz/theme.ts @@ -24,13 +24,17 @@ export interface Theme { const DEFAULT_SANS_SERIF = '-apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif' const DEFAULT_MONO = "ui-monospace, SFMono-Regular, SF Mono, Menlo, monospace" + export function googleFontHref(theme: Theme) { const { code, header, body } = theme.typography return `https://fonts.googleapis.com/css2?family=${code}&family=${header}:wght@400;700&family=${body}:ital,wght@0,400;0,600;1,400;1,600&display=swap` } export function joinStyles(theme: Theme, ...stylesheet: string[]) { - return `:root { + return ` +${stylesheet.join("\n\n")} + +:root { --light: ${theme.colors.lightMode.light}; --lightgray: ${theme.colors.lightMode.lightgray}; --gray: ${theme.colors.lightMode.gray}; @@ -55,6 +59,6 @@ export function joinStyles(theme: Theme, ...stylesheet: string[]) { --tertiary: ${theme.colors.darkMode.tertiary}; --highlight: ${theme.colors.darkMode.highlight}; } +` -${stylesheet.join("\n\n")}` }