Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/check-commits.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 22
node-version: 22.22.1
- name: Install dependencies
run: npm install
- name: Ensure Git is installed
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/test-and-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
node-version: [20, 22]
node-version: [20, 22.22.1]

steps:
- uses: actions/checkout@v4
Expand Down Expand Up @@ -51,7 +51,7 @@ jobs:
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 22
node-version: 22.22.1
- name: Install dependencies
run: npm clean-install

Expand Down
80 changes: 80 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
"@octokit/rest": "^22.0.0",
"@open-wc/dev-server-hmr": "^0.2.0",
"@open-wc/testing": "^4.0.0",
"@rollup/plugin-commonjs": "^28.0.3",
"@rollup/plugin-node-resolve": "^16.0.1",
"@rollup/plugin-terser": "^0.4.4",
"@rollup/plugin-typescript": "^12.1.4",
Expand Down
1 change: 1 addition & 0 deletions src/commands/_sharedOptions.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export function withBuildOptions(command) {
.option("-m, --module-paths [paths...]", "Path(s) to node_modules folder")
.option("-w, --watch", "Watches for changes")
.option("--skip-docs", "Skip documentation generation", false)
.option("-r, --readme-template <url>", "URL to the README template file")
.option(
"--wca-input [files...]",
"Source file(s) to analyze for API documentation",
Expand Down
8 changes: 7 additions & 1 deletion src/commands/docs.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import { program } from "commander";
import { api, cem, docs, serve } from "#scripts/docs/index.ts";
import { api, cem, docs, serve, watchDocs } from "#scripts/docs/index.ts";
import { withServerOptions } from "#commands/_sharedOptions.js";

let docsCommand = program
.command("docs")
.description("Generate API documentation")
.option("-c, --cem", "Generate Custom Elements Manifest (CEM) file", false)
.option("-a, --api", "Creates api md file from CEM", false)
.option("-w, --watch", "Watch for changes and rebuild docs", false)
.option("-r, --readme-template <url>", "URL to the README template file")
.option("--skip-readme", "Skip README.md processing", false)

docsCommand = withServerOptions(docsCommand);
Expand All @@ -27,4 +29,8 @@ let docsCommand = program
await serve(options);
}

if (options.watch) {
await watchDocs(options);
}

});
170 changes: 167 additions & 3 deletions src/scripts/build/bundleHandlers.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
import { rmSync } from "node:fs";
import { join } from "node:path";
import { existsSync, readFileSync, rmSync, writeFileSync } from "node:fs";
import { basename, dirname, join, resolve } from "node:path";
import { pathToFileURL } from "node:url";
import { glob } from "glob";
import ora from "ora";
import { rollup } from "rollup";
import * as sass from "sass";
import { analyzeComponents } from "#scripts/analyze.js";
import { runDefaultDocsBuild } from "#scripts/build/defaultDocsBuild.js";
import { getDemoConfig } from "#scripts/build/configUtils.js";
import { MODULE_DIRS } from "#scripts/build/paths.js";
import { copyReadmeToDemo } from "#utils/copyReadmeToDemo.js";

/**
* Clean up the dist folder
Expand Down Expand Up @@ -108,9 +114,167 @@ export async function generateDocs(options) {
"Analyzing components and making docs...",
async () => {
await analyzeComponents(sourceFiles, outFile);
await runDefaultDocsBuild();
await runDefaultDocsBuild(options);
copyReadmeToDemo();
},
"Docs ready! Looking good.",
"Doc troubles!",
);
}



/**
* Sass FileImporter that resolves bare package imports from node_modules
* and redirects hoisted dependencies. Returns file: URLs so that
* within-package relative imports resolve natively on disk. When a
* relative import fails (e.g. ./../../node_modules/@pkg/foo pointing at
* a hoisted location that doesn't exist), Sass falls back to findFileUrl,
* where we redirect to the actual hoisted location.
*/
function createNodeModulesImporter() {
const cwd = process.cwd();

function tryResolve(filePath) {
const candidates = [
filePath,
`${filePath}.scss`,
`${filePath}.css`,
join(dirname(filePath), `_${basename(filePath)}.scss`),
join(filePath, "_index.scss"),
join(filePath, "index.scss"),
];
return candidates.find((c) => existsSync(c));
}

function findInModuleDirs(pkgPath) {
for (const dir of MODULE_DIRS) {
const found = tryResolve(resolve(cwd, dir, pkgPath));
if (found) return found;
}

// Try resolving via package.json "exports" map
const exportResolved = resolveViaExports(pkgPath);
if (exportResolved) return exportResolved;

return null;
}

/**
* Resolves a bare specifier using the package.json "exports" field.
* e.g. "@scope/pkg/demo-styles" looks up "./demo-styles" in the exports map
* of @scope/pkg/package.json and resolves the mapped path.
*/
function resolveViaExports(pkgPath) {
let pkgName;
let subpath;

if (pkgPath.startsWith("@")) {
const parts = pkgPath.split("/");
if (parts.length < 3) return null;
pkgName = `${parts[0]}/${parts[1]}`;
subpath = `./${parts.slice(2).join("/")}`;
} else {
const slashIdx = pkgPath.indexOf("/");
if (slashIdx === -1) return null;
pkgName = pkgPath.slice(0, slashIdx);
subpath = `./${pkgPath.slice(slashIdx + 1)}`;
}

for (const dir of MODULE_DIRS) {
const pkgJsonPath = resolve(cwd, dir, pkgName, "package.json");
if (!existsSync(pkgJsonPath)) continue;

try {
const pkgJson = JSON.parse(readFileSync(pkgJsonPath, "utf-8"));
const exports = pkgJson.exports;
if (!exports || typeof exports !== "object") continue;

const mapped = exports[subpath];
if (!mapped) continue;

const target = typeof mapped === "string" ? mapped : mapped.default || mapped.import;
if (!target) continue;

const resolved = tryResolve(resolve(cwd, dir, pkgName, target));
if (resolved) return resolved;
} catch {
continue;
}
}

return null;
}

return {
findFileUrl(url) {
// Failed relative import containing a node_modules path that
// doesn't exist due to hoisting — redirect to hoisted location
if (url.includes("/node_modules/")) {
const lastIdx = url.lastIndexOf("/node_modules/");
const pkgPath = url.slice(lastIdx + "/node_modules/".length);
const found = findInModuleDirs(pkgPath);
if (found) return pathToFileURL(found);
}

// Bare package import (e.g. @aurodesignsystem/webcorestylesheets/...)
if (!url.startsWith(".") && !url.startsWith("/") && !url.startsWith("file:")) {
const found = findInModuleDirs(url);
if (found) return pathToFileURL(found);
}

return null;
},
};
}

/**
* Compiles all SCSS files in the demo directory to CSS.
* @param {string} [demoDir="./demo"] - Path to the demo directory
*/
export async function compileDemoScss(demoDir = "./demo") {
return runBuildStep(
"Compiling demo SCSS...",
async () => {
const scssFiles = glob.sync(join(demoDir, "**/*.scss"));
const importer = createNodeModulesImporter();
const cwd = process.cwd();
const loadPaths = MODULE_DIRS.map((dir) => resolve(cwd, dir));

for (const scssFile of scssFiles) {
const result = sass.compile(scssFile, {
importers: [importer],
loadPaths,
silenceDeprecations: ["import"],
style: "compressed",
});

const cssFile = scssFile.replace(/\.scss$/, ".min.css");
writeFileSync(cssFile, result.css);
}

return scssFiles.length;
},
"Demo SCSS compiled.",
"SCSS compilation failed.",
);
}

/**
* Bundles demo JS files to minified ESM output.
* @param {object} [options={}] - Options passed to getDemoConfig
*/
export async function buildDemoBundle(options = {}) {
const demoConfig = getDemoConfig(options);

return runBuildStep(
"Bundling demo JS...",
async () => {
const bundle = await rollup(demoConfig.config);
await bundle.write(demoConfig.config.output);
await bundle.close();
},
"Demo JS bundled.",
"Demo JS bundling failed.",
);
}
3 changes: 3 additions & 0 deletions src/scripts/build/configUtils.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { basename, join } from "node:path";
import commonjs from "@rollup/plugin-commonjs";
import { nodeResolve } from "@rollup/plugin-node-resolve";
import { glob } from "glob";
import { litScss } from "rollup-plugin-scss-lit";
Expand Down Expand Up @@ -33,6 +34,7 @@ export function getPluginsConfig(modulePaths = [], options = {}) {
preferBuiltins: false,
moduleDirectories: DEFAULTS.moduleDirectories,
}),
commonjs(),
litScss({
// Disable CSS minification in dev for readability and faster rebuilds
minify: dev ? false : { fast: true },
Expand Down Expand Up @@ -146,6 +148,7 @@ export function getWatcherConfig(watchOptions) {
"**/custom-elements.json",
"**/demo/*.md",
"**/demo/**/*.min.js",
"**/demo/**/*.min.css",
"**/docs/api.md",
"**/node_modules/**",
"**/.git/**",
Expand Down
Loading
Loading