diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs new file mode 100644 index 00000000..88c65566 --- /dev/null +++ b/.git-blame-ignore-revs @@ -0,0 +1,8 @@ +# Revisions to ignore in `git blame`. +# +# Enable locally with: +# git config blame.ignoreRevsFile .git-blame-ignore-revs +# GitHub/GitLab pick this file up automatically. + +# style: reformat with prettier (wider printWidth) +dc5d58c798e8c6d2d4674db58c262564e69cd779 diff --git a/.oxlintrc.json b/.oxlintrc.json deleted file mode 100644 index 1ece3551..00000000 --- a/.oxlintrc.json +++ /dev/null @@ -1,156 +0,0 @@ -{ - "$schema": "./node_modules/oxlint/configuration_schema.json", - "plugins": ["typescript", "unicorn"], - "categories": { - "correctness": "off" - }, - "env": { - "builtin": true, - "browser": true, - "commonjs": true, - "node": true, - "shared-node-browser": true - }, - "ignorePatterns": [ - "**/logs", - "**/*.log", - "**/npm-debug.log*", - "**/yarn-debug.log*", - "**/yarn-error.log*", - "**/pnpm-debug.log*", - "**/lerna-debug.log*", - "**/node_modules", - "**/dist", - "**/dist-ssr", - "**/*.local", - ".vscode/*", - "!.vscode/extensions.json", - "**/.idea", - "**/.DS_Store", - "**/*.suo", - "**/*.ntvs*", - "**/*.njsproj", - "**/*.sln", - "**/*.sw?", - "src/generatedContent", - "**/archive", - "**/vite.config.js.timestamp*", - "content/**" - ], - "rules": { - "for-direction": "error", - "no-async-promise-executor": "error", - "no-case-declarations": "error", - "no-class-assign": "error", - "no-compare-neg-zero": "error", - "no-cond-assign": "error", - "no-const-assign": "error", - "no-constant-binary-expression": "error", - "no-constant-condition": "error", - "no-control-regex": "error", - "no-debugger": "error", - "no-delete-var": "error", - "no-dupe-class-members": "error", - "no-dupe-else-if": "error", - "no-dupe-keys": "error", - "no-duplicate-case": "error", - "no-empty": "error", - "no-empty-character-class": "error", - "no-empty-pattern": "error", - "no-empty-static-block": "error", - "no-ex-assign": "error", - "no-extra-boolean-cast": "error", - "no-fallthrough": "error", - "no-func-assign": "error", - "no-global-assign": "error", - "no-import-assign": "error", - "no-invalid-regexp": "error", - "no-irregular-whitespace": "error", - "no-loss-of-precision": "error", - "no-new-native-nonconstructor": "error", - "no-nonoctal-decimal-escape": "error", - "no-obj-calls": "error", - "no-prototype-builtins": "error", - "no-redeclare": "error", - "no-regex-spaces": "error", - "no-self-assign": "error", - "no-setter-return": "error", - "no-shadow-restricted-names": "error", - "no-sparse-arrays": "error", - "no-this-before-super": "error", - "no-unexpected-multiline": "off", - "no-unsafe-finally": "error", - "no-unsafe-negation": "error", - "no-unsafe-optional-chaining": "error", - "no-unused-labels": "error", - "no-unused-private-class-members": "error", - "no-unused-vars": "error", - "no-useless-backreference": "error", - "no-useless-catch": "error", - "no-useless-escape": "error", - "no-with": "error", - "require-yield": "error", - "use-isnan": "error", - "valid-typeof": "error", - "@typescript-eslint/ban-ts-comment": "error", - "no-array-constructor": "error", - "@typescript-eslint/no-duplicate-enum-values": "error", - "@typescript-eslint/no-empty-object-type": "error", - "@typescript-eslint/no-explicit-any": "error", - "@typescript-eslint/no-extra-non-null-assertion": "error", - "@typescript-eslint/no-misused-new": "error", - "@typescript-eslint/no-namespace": "error", - "@typescript-eslint/no-non-null-asserted-optional-chain": "error", - "@typescript-eslint/no-require-imports": "error", - "@typescript-eslint/no-this-alias": "error", - "@typescript-eslint/no-unnecessary-type-constraint": "error", - "@typescript-eslint/no-unsafe-declaration-merging": "error", - "@typescript-eslint/no-unsafe-function-type": "error", - "no-unused-expressions": "error", - "@typescript-eslint/no-wrapper-object-types": "error", - "@typescript-eslint/prefer-as-const": "error", - "@typescript-eslint/prefer-namespace-keyword": "error", - "@typescript-eslint/triple-slash-reference": "error", - "curly": "off", - "unicorn/empty-brace-spaces": "off", - "unicorn/no-nested-ternary": "off", - "unicorn/number-literal-case": "off" - }, - "overrides": [ - { - "files": ["**/*.ts", "**/*.tsx", "**/*.mts", "**/*.cts"], - "rules": { - "no-class-assign": "off", - "no-const-assign": "off", - "no-dupe-class-members": "off", - "no-dupe-keys": "off", - "no-func-assign": "off", - "no-import-assign": "off", - "no-new-native-nonconstructor": "off", - "no-obj-calls": "off", - "no-redeclare": "off", - "no-setter-return": "off", - "no-this-before-super": "off", - "no-unsafe-negation": "off", - "no-var": "error", - "no-with": "off", - "prefer-rest-params": "error", - "prefer-spread": "error" - } - }, - { - "files": ["*.svelte", "**/*.svelte"], - "rules": { - "no-inner-declarations": "off", - "no-self-assign": "off" - } - }, - { - "files": ["*.svelte", "**/*.svelte"], - "rules": { - "no-inner-declarations": "off", - "no-self-assign": "off" - } - } - ] -} diff --git a/.prettierrc b/.prettierrc deleted file mode 100644 index 05dafbf8..00000000 --- a/.prettierrc +++ /dev/null @@ -1,11 +0,0 @@ -{ - "plugins": ["prettier-plugin-svelte"], - "overrides": [ - { - "files": "*.svelte", - "options": { - "parser": "svelte" - } - } - ] -} diff --git a/.vite-hooks/pre-commit b/.vite-hooks/pre-commit new file mode 100755 index 00000000..85fb65b4 --- /dev/null +++ b/.vite-hooks/pre-commit @@ -0,0 +1 @@ +vp staged diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 3758820d..44c4bb94 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,7 +1,3 @@ { - "recommendations": [ - "esbenp.prettier-vscode", - "svelte.svelte-vscode", - "bradlc.vscode-tailwindcss" - ] + "recommendations": ["esbenp.prettier-vscode", "svelte.svelte-vscode", "bradlc.vscode-tailwindcss"] } diff --git a/README.md b/README.md index 5def79f2..f73648f7 100644 --- a/README.md +++ b/README.md @@ -711,6 +711,12 @@ pnpm run dev This project requires Node.js to be `v22.18.0` or higher. +Bulk formatting commits are listed in `.git-blame-ignore-revs` so they don't clutter `git blame`. Enable it locally with: + +```bash +git config blame.ignoreRevsFile .git-blame-ignore-revs +``` + ### Principle when add/edit a framework snippet > Clarity is better than optimization for Component Party diff --git a/build/generateContentVitePlugin.ts b/build/generateContentVitePlugin.ts index 3f191b6b..45ba0712 100644 --- a/build/generateContentVitePlugin.ts +++ b/build/generateContentVitePlugin.ts @@ -38,12 +38,15 @@ export default function pluginGenerateFrameworkContent() { } let fsContentWatcher: FSWatcher | undefined; - if (process.env.NODE_ENV === "development") { - fsContentWatcher = chokidar.watch(["content"]).on("change", build); - } return { name, + configureServer(server: { httpServer?: { once(e: string, cb: () => void): void } }): void { + fsContentWatcher = chokidar.watch(["content"]).on("change", build); + server.httpServer?.once("close", () => { + void fsContentWatcher?.close(); + }); + }, async buildStart(): Promise { try { await build(); diff --git a/build/lib/angularHighlighter.ts b/build/lib/angularHighlighter.ts index 5c72bdcb..1ef6c9b1 100644 --- a/build/lib/angularHighlighter.ts +++ b/build/lib/angularHighlighter.ts @@ -1,5 +1,3 @@ export function mustUseAngularHighlighter(fileContent: string): boolean { - return ( - fileContent.includes("@angular/core") && fileContent.includes("template") - ); + return fileContent.includes("@angular/core") && fileContent.includes("template"); } diff --git a/build/lib/componentPartyShikiTheme.ts b/build/lib/componentPartyShikiTheme.ts index e8aff1e3..3a56e9f3 100644 --- a/build/lib/componentPartyShikiTheme.ts +++ b/build/lib/componentPartyShikiTheme.ts @@ -253,8 +253,7 @@ export const componentPartyShikiTheme = { }, { name: "java modifier.import", - scope: - "storage.modifier.import.java,storage.type.java,storage.type.generic.java", + scope: "storage.modifier.import.java,storage.type.java,storage.type.generic.java", settings: { foreground: "#e5c07b", }, @@ -296,8 +295,7 @@ export const componentPartyShikiTheme = { }, { name: "support.constant.property-value.scss", - scope: - "support.constant.property-value.scss,support.constant.property-value.css", + scope: "support.constant.property-value.scss,support.constant.property-value.css", settings: { foreground: "#d19a66", }, @@ -340,8 +338,7 @@ export const componentPartyShikiTheme = { }, { name: "js/ts module", - scope: - "support.module.node,support.type.object.module,support.module.node", + scope: "support.module.node,support.type.object.module,support.module.node", settings: { foreground: "#e5c07b", }, @@ -547,8 +544,7 @@ export const componentPartyShikiTheme = { }, { name: "Compound Assignment Operators js/ts", - scope: - "keyword.operator.assignment.compound.js,keyword.operator.assignment.compound.ts", + scope: "keyword.operator.assignment.compound.js,keyword.operator.assignment.compound.ts", settings: { foreground: "#56b6c2", }, @@ -651,11 +647,7 @@ export const componentPartyShikiTheme = { }, { name: "Class name", - scope: [ - "entity.name.class", - "variable.other.class.js", - "variable.other.class.ts", - ], + scope: ["entity.name.class", "variable.other.class.js", "variable.other.class.ts"], settings: { foreground: "#e5c07b", }, @@ -846,8 +838,7 @@ export const componentPartyShikiTheme = { }, { name: "Headings", - scope: - "markup.heading punctuation.definition.heading, entity.name.section", + scope: "markup.heading punctuation.definition.heading, entity.name.section", settings: { foreground: "#61afef", }, @@ -977,16 +968,14 @@ export const componentPartyShikiTheme = { }, { name: "[VSCODE-CUSTOM] Markdown Underline Link/Image", - scope: - "markup.underline.link.markdown,markup.underline.link.image.markdown", + scope: "markup.underline.link.markdown,markup.underline.link.image.markdown", settings: { foreground: "#c678dd", }, }, { name: "[VSCODE-CUSTOM] Markdown Link Title/Description", - scope: - "string.other.link.title.markdown,string.other.link.description.markdown", + scope: "string.other.link.title.markdown,string.other.link.description.markdown", settings: { foreground: "#61afef", }, @@ -1042,8 +1031,7 @@ export const componentPartyShikiTheme = { }, { name: "Embedded", - scope: - "punctuation.section.embedded.begin,punctuation.section.embedded.end", + scope: "punctuation.section.embedded.begin,punctuation.section.embedded.end", settings: { foreground: "#c678dd", }, @@ -1105,8 +1093,7 @@ export const componentPartyShikiTheme = { }, { name: "Source Json Meta Structure Dictionary Json > String Quoted Json > Punctuation String", - scope: - "source.json meta.structure.dictionary.json > string.quoted.json > punctuation.string", + scope: "source.json meta.structure.dictionary.json > string.quoted.json > punctuation.string", settings: { foreground: "#e06c75", }, @@ -1143,16 +1130,14 @@ export const componentPartyShikiTheme = { }, { name: "laravel blade tag", - scope: - "text.html.laravel-blade source.php.embedded.line.html entity.name.tag.laravel-blade", + scope: "text.html.laravel-blade source.php.embedded.line.html entity.name.tag.laravel-blade", settings: { foreground: "#c678dd", }, }, { name: "laravel blade @", - scope: - "text.html.laravel-blade source.php.embedded.line.html support.constant.laravel-blade", + scope: "text.html.laravel-blade source.php.embedded.line.html support.constant.laravel-blade", settings: { foreground: "#c678dd", }, @@ -1210,8 +1195,7 @@ export const componentPartyShikiTheme = { }, { name: "php call-function", - scope: - "meta.function-call.php,meta.function-call.object.php,meta.function-call.static.php", + scope: "meta.function-call.php,meta.function-call.object.php,meta.function-call.static.php", settings: { foreground: "#61afef", }, @@ -1284,8 +1268,7 @@ export const componentPartyShikiTheme = { }, { name: "python function support", - scope: - "support.token.decorator.python,meta.function.decorator.identifier.python", + scope: "support.token.decorator.python,meta.function.decorator.identifier.python", settings: { foreground: "#56b6c2", }, @@ -1801,10 +1784,7 @@ export const componentPartyShikiTheme = { }, }, { - scope: [ - "constant.language.symbol.elixir", - "constant.language.symbol.double-quoted.elixir", - ], + scope: ["constant.language.symbol.elixir", "constant.language.symbol.double-quoted.elixir"], settings: { foreground: "#56b6c2", }, @@ -1844,10 +1824,7 @@ export const componentPartyShikiTheme = { }, { name: "punctuation.section.embedded.begin.php", - scope: [ - "punctuation.section.embedded.begin.php", - "punctuation.section.embedded.end.php", - ], + scope: ["punctuation.section.embedded.begin.php", "punctuation.section.embedded.end.php"], settings: { foreground: "#BE5046", }, diff --git a/build/lib/generateContent.ts b/build/lib/generateContent.ts index 1a377c22..11cb0aab 100644 --- a/build/lib/generateContent.ts +++ b/build/lib/generateContent.ts @@ -5,10 +5,7 @@ import { frameworks } from "../../frameworks.ts"; import playgroundUrlByFramework from "./playgroundUrlByFramework.ts"; import prettier from "prettier"; import { mustUseAngularHighlighter } from "./angularHighlighter.ts"; -import { - codeToHighlightCodeHtml, - markdownToHighlightedHtml, -} from "./highlighter.ts"; +import { codeToHighlightCodeHtml, markdownToHighlightedHtml } from "./highlighter.ts"; import kebabCase from "just-kebab-case"; interface File { @@ -110,9 +107,7 @@ async function shouldRegenerateFiles( return false; } -export default async function generateContent( - options: { noCache?: boolean } = {}, -): Promise { +export default async function generateContent(options: { noCache?: boolean } = {}): Promise { const rootDir = await packageDirectory(); if (!rootDir) { throw new Error("Could not find package directory"); @@ -215,9 +210,7 @@ export default async function generateContent( } if (frameworkSnippet.files.length > 0) { - const frameworkConfig = frameworks.find( - (f) => f.id === frameworkId, - ); + const frameworkConfig = frameworks.find((f) => f.id === frameworkId); if (frameworkConfig) { frameworkSnippet.files = frameworkConfig.filesSorter( frameworkSnippet.files as unknown as FrameworkFile[], @@ -299,10 +292,7 @@ export default async function generateContent( await Promise.all( Object.keys(byFrameworkId).map((frameworkId) => { - const frameworkFilePath = path.join( - frameworkDirPath, - `${frameworkId}.js`, - ); + const frameworkFilePath = path.join(frameworkDirPath, `${frameworkId}.js`); return writeJsFile( frameworkFilePath, ` @@ -319,10 +309,7 @@ export default async function generateContent( ${commentDisclaimer} export default { ${Object.keys(byFrameworkId) - .map( - (frameworkId) => - `${frameworkId}: () => import("./${frameworkId}.js")`, - ) + .map((frameworkId) => `${frameworkId}: () => import("./${frameworkId}.js")`) .join(",\n")} }; @@ -426,7 +413,5 @@ ${redirects.join("\n")} const redirectsFilePath = path.join(publicDir, "_redirects"); await fs.writeFile(redirectsFilePath, redirectsContent); - console.info( - `Generated _redirects file for Cloudflare Pages with ${redirects.length} redirects`, - ); + console.info(`Generated _redirects file for Cloudflare Pages with ${redirects.length} redirects`); } diff --git a/build/lib/highlighter.d.ts b/build/lib/highlighter.d.ts index 0f6dbf13..c03f8752 100644 --- a/build/lib/highlighter.d.ts +++ b/build/lib/highlighter.d.ts @@ -1,7 +1,4 @@ -export function codeToHighlightCodeHtml( - code: string, - language: string, -): Promise; +export function codeToHighlightCodeHtml(code: string, language: string): Promise; export function markdownToHighlightedHtml(markdown: string): Promise; diff --git a/build/lib/highlighter.ts b/build/lib/highlighter.ts index 5cb035b7..c05006c6 100644 --- a/build/lib/highlighter.ts +++ b/build/lib/highlighter.ts @@ -9,16 +9,11 @@ import { fromHighlighter } from "@shikijs/markdown-it/core"; import { componentPartyShikiTheme } from "./componentPartyShikiTheme.ts"; // Singleton instances -let highlighter: HighlighterGeneric | null = - null; +let highlighter: HighlighterGeneric | null = null; let md: MarkdownIt | null = null; -let highlighterPromise: Promise< - HighlighterGeneric -> | null = null; +let highlighterPromise: Promise> | null = null; -async function getHighlighter(): Promise< - HighlighterGeneric -> { +async function getHighlighter(): Promise> { if (!highlighterPromise) { highlighterPromise = createHighlighter({ themes: [componentPartyShikiTheme], @@ -64,10 +59,7 @@ async function getMarkdownIt(): Promise { return md; } -export async function codeToHighlightCodeHtml( - code: string, - lang: string, -): Promise { +export async function codeToHighlightCodeHtml(code: string, lang: string): Promise { const highlighterInstance = await getHighlighter(); const html = await highlighterInstance.codeToHtml(code, { lang, @@ -77,9 +69,7 @@ export async function codeToHighlightCodeHtml( return html; } -export async function markdownToHighlightedHtml( - markdownText: string, -): Promise { +export async function markdownToHighlightedHtml(markdownText: string): Promise { const mdInstance = await getMarkdownIt(); const html = mdInstance.render(markdownText); return html; diff --git a/build/lib/playgroundUrlByFramework.ts b/build/lib/playgroundUrlByFramework.ts index 5c3d57c4..1bc0cff3 100644 --- a/build/lib/playgroundUrlByFramework.ts +++ b/build/lib/playgroundUrlByFramework.ts @@ -54,20 +54,14 @@ const playgroundUrlByFramework: Record = { const url = generateURLFromData(data); return url; }, - svelte4: async ( - contentByFilename: Record, - title?: string, - ) => { + svelte4: async (contentByFilename: Record, title?: string) => { return generateSveltePlaygroundURL({ version: 4, contentByFilename, title, }); }, - svelte5: async ( - contentByFilename: Record, - title?: string, - ) => { + svelte5: async (contentByFilename: Record, title?: string) => { return generateSveltePlaygroundURL({ version: 5, contentByFilename, @@ -75,8 +69,7 @@ const playgroundUrlByFramework: Record = { }); }, alpine: (contentByFilename: Record) => { - const BASE_URL = - "https://codesandbox.io/api/v1/sandboxes/define?embed=1¶meters="; + const BASE_URL = "https://codesandbox.io/api/v1/sandboxes/define?embed=1¶meters="; const BASE_PREFIX = `\n\n \n \n \n \n Alpine.js Playground\n \n \n \n\n`; const BASE_SUFFIX = `\n \n`; @@ -87,8 +80,7 @@ const playgroundUrlByFramework: Record = { content: { dependencies: {} }, }, "index.html": { - content: - BASE_PREFIX + (contentByFilename["index.html"] || "") + BASE_SUFFIX, + content: BASE_PREFIX + (contentByFilename["index.html"] || "") + BASE_SUFFIX, }, "sandbox.config.json": { content: '{\n "template": "static"\n}', @@ -124,8 +116,7 @@ const playgroundUrlByFramework: Record = { const mainComponentName = mainFile.name; mainFile.name = "main"; mainFile.type = "tsx"; - mainFile.source = - SOURCE_PREFIX + mainFile.source + getSourceSuffix(mainComponentName); + mainFile.source = SOURCE_PREFIX + mainFile.source + getSourceSuffix(mainComponentName); return generateURLFromData(data); }, @@ -136,10 +127,7 @@ const playgroundUrlByFramework: Record = { content, })); - return ( - "https://markojs.com/playground#" + - (await markoCompress(JSON.stringify(data))) - ); + return "https://markojs.com/playground#" + (await markoCompress(JSON.stringify(data))); }, ripple: (contentByFilename: Record, title?: string) => { return generateRipplePlaygroundURL(contentByFilename, title); @@ -186,10 +174,7 @@ async function generateSveltePlaygroundURL({ // method `compress_and_encode_text` from https://github.com/sveltejs/svelte.dev/blob/main/apps/svelte.dev/src/routes/(authed)/playground/%5Bid%5D/gzip.js async function compress_and_encode_text(input: string): Promise { - const reader = new Blob([input]) - .stream() - .pipeThrough(new CompressionStream("gzip")) - .getReader(); + const reader = new Blob([input]).stream().pipeThrough(new CompressionStream("gzip")).getReader(); let buffer = ""; for (;;) { const { done, value } = await reader.read(); @@ -223,10 +208,7 @@ export async function markoCompress(value: string): Promise { } } - return btoa(result) - .replace(/=+$/, "") - .replace(/\+/g, "-") - .replace(/\//g, "_"); + return btoa(result).replace(/=+$/, "").replace(/\+/g, "-").replace(/\//g, "_"); } finally { reader.releaseLock(); } @@ -239,9 +221,7 @@ function generateRipplePlaygroundURL( function mergeRippleFiles(sourceByFilename: Record): string { const scriptFilenames = Object.keys(sourceByFilename).filter( (filename) => - filename.endsWith(".tsrx") || - filename.endsWith(".js") || - filename.endsWith(".ts"), + filename.endsWith(".tsrx") || filename.endsWith(".js") || filename.endsWith(".ts"), ); if (scriptFilenames.length === 0) { @@ -249,9 +229,7 @@ function generateRipplePlaygroundURL( } return scriptFilenames - .map((filename) => - stripLocalRippleImports(sourceByFilename[filename]).trim(), - ) + .map((filename) => stripLocalRippleImports(sourceByFilename[filename]).trim()) .filter(Boolean) .join("\n\n"); } @@ -260,19 +238,13 @@ function generateRipplePlaygroundURL( return source.replace( /^\s*import\s+[^"']+from\s+["'](.+?)["'];?\s*$/gm, (fullMatch: string, specifier: string) => { - return specifier.startsWith(".") && isLocalScriptImport(specifier) - ? "" - : fullMatch; + return specifier.startsWith(".") && isLocalScriptImport(specifier) ? "" : fullMatch; }, ); } function isLocalScriptImport(specifier: string): boolean { - return ( - specifier.endsWith(".tsrx") || - specifier.endsWith(".js") || - specifier.endsWith(".ts") - ); + return specifier.endsWith(".tsrx") || specifier.endsWith(".js") || specifier.endsWith(".ts"); } const filenames = Object.keys(contentByFilename); diff --git a/build/template/footer.html b/build/template/footer.html index 7f04f700..d49034ef 100644 --- a/build/template/footer.html +++ b/build/template/footer.html @@ -8,8 +8,7 @@

Footer

Component party

- Web component JavaScript frameworks overview by their syntax and - features + Web component JavaScript frameworks overview by their syntax and features

-
+
<% for (const navigation of it.navigations) { %>