Sync generated provider output

This commit is contained in:
github-actions[bot]
2026-08-26 05:31:15 +00:00
parent 8f416aa760
commit ba68dce41e
16 changed files with 608 additions and 80 deletions
@@ -964,8 +964,34 @@ function buildStaticWindow(staticDoc) {
};
}
function resolveLinkedCssPath(fileDir, href) {
const stripped = href.split(/[?#]/)[0];
const rootRelative = stripped.startsWith('/') && !stripped.startsWith('//');
if (!rootRelative) return path.resolve(fileDir, stripped);
// Drop "." and reject ".." so /../outside.css cannot walk out of dir.
const segments = stripped.replace(/^\/+/, '').split(/[/\\]/).filter(p => p && p !== '.');
if (segments.some(p => p === '..')) return path.join(fileDir, segments.filter(p => p !== '..').join(path.sep));
const rel = segments.join(path.sep);
let dir = fileDir;
for (;;) {
const parent = path.dirname(dir);
if (parent === dir) break; // never use the filesystem root as document root
try {
const candidate = path.join(dir, rel);
if (fs.statSync(candidate).isFile()) return candidate;
} catch { /* missing or unreadable candidate */ }
// Stop at the project root so a coincidental ~/static/app.css cannot win.
try {
if (fs.existsSync(path.join(dir, 'package.json')) || fs.existsSync(path.join(dir, '.git'))) break;
} catch { /* unreadable marker */ }
dir = parent;
}
return path.join(fileDir, rel);
}
function collectStaticCssText(root, fileDir, profile, filePath, modules) {
const styleTexts = [];
const warnedMissingStylesheets = new Set();
for (const styleEl of modules.selectAll('style', root.children || [])) {
styleTexts.push(modules.domutils.textContent(styleEl));
}
@@ -974,10 +1000,10 @@ function collectStaticCssText(root, fileDir, profile, filePath, modules) {
const rel = link.attribs?.rel || '';
const href = link.attribs?.href || '';
if (!/\bstylesheet\b/i.test(rel) || !href || /^(https?:)?\/\//i.test(href)) continue;
// Cache-busting hrefs (styles.css?v=3) resolve to the file, not to a
// literal path with the query in it; a versioned link otherwise made the
// whole stylesheet invisible to every element-level check.
const cssPath = path.resolve(fileDir, href.split(/[?#]/)[0]);
// Cache-busting (styles.css?v=3) and root-relative (/static/app.css) hrefs
// must not resolve as OS-absolute paths; otherwise the whole stylesheet is
// invisible to every element-level check.
const cssPath = resolveLinkedCssPath(fileDir, href);
try {
const css = profileStep(profile, {
engine: 'static-html',
@@ -987,7 +1013,14 @@ function collectStaticCssText(root, fileDir, profile, filePath, modules) {
detail: href,
}, () => fs.readFileSync(cssPath, 'utf-8'));
styleTexts.push(css);
} catch { /* skip unreadable */ }
} catch {
if (!warnedMissingStylesheets.has(cssPath)) {
warnedMissingStylesheets.add(cssPath);
process.stderr.write(
`impeccable detect: could not read linked stylesheet ${href} (resolved to ${cssPath}); color and custom-property rules will be incomplete\n`
);
}
}
}
return styleTexts.join('\n');
}
@@ -964,8 +964,34 @@ function buildStaticWindow(staticDoc) {
};
}
function resolveLinkedCssPath(fileDir, href) {
const stripped = href.split(/[?#]/)[0];
const rootRelative = stripped.startsWith('/') && !stripped.startsWith('//');
if (!rootRelative) return path.resolve(fileDir, stripped);
// Drop "." and reject ".." so /../outside.css cannot walk out of dir.
const segments = stripped.replace(/^\/+/, '').split(/[/\\]/).filter(p => p && p !== '.');
if (segments.some(p => p === '..')) return path.join(fileDir, segments.filter(p => p !== '..').join(path.sep));
const rel = segments.join(path.sep);
let dir = fileDir;
for (;;) {
const parent = path.dirname(dir);
if (parent === dir) break; // never use the filesystem root as document root
try {
const candidate = path.join(dir, rel);
if (fs.statSync(candidate).isFile()) return candidate;
} catch { /* missing or unreadable candidate */ }
// Stop at the project root so a coincidental ~/static/app.css cannot win.
try {
if (fs.existsSync(path.join(dir, 'package.json')) || fs.existsSync(path.join(dir, '.git'))) break;
} catch { /* unreadable marker */ }
dir = parent;
}
return path.join(fileDir, rel);
}
function collectStaticCssText(root, fileDir, profile, filePath, modules) {
const styleTexts = [];
const warnedMissingStylesheets = new Set();
for (const styleEl of modules.selectAll('style', root.children || [])) {
styleTexts.push(modules.domutils.textContent(styleEl));
}
@@ -974,10 +1000,10 @@ function collectStaticCssText(root, fileDir, profile, filePath, modules) {
const rel = link.attribs?.rel || '';
const href = link.attribs?.href || '';
if (!/\bstylesheet\b/i.test(rel) || !href || /^(https?:)?\/\//i.test(href)) continue;
// Cache-busting hrefs (styles.css?v=3) resolve to the file, not to a
// literal path with the query in it; a versioned link otherwise made the
// whole stylesheet invisible to every element-level check.
const cssPath = path.resolve(fileDir, href.split(/[?#]/)[0]);
// Cache-busting (styles.css?v=3) and root-relative (/static/app.css) hrefs
// must not resolve as OS-absolute paths; otherwise the whole stylesheet is
// invisible to every element-level check.
const cssPath = resolveLinkedCssPath(fileDir, href);
try {
const css = profileStep(profile, {
engine: 'static-html',
@@ -987,7 +1013,14 @@ function collectStaticCssText(root, fileDir, profile, filePath, modules) {
detail: href,
}, () => fs.readFileSync(cssPath, 'utf-8'));
styleTexts.push(css);
} catch { /* skip unreadable */ }
} catch {
if (!warnedMissingStylesheets.has(cssPath)) {
warnedMissingStylesheets.add(cssPath);
process.stderr.write(
`impeccable detect: could not read linked stylesheet ${href} (resolved to ${cssPath}); color and custom-property rules will be incomplete\n`
);
}
}
}
return styleTexts.join('\n');
}
@@ -964,8 +964,34 @@ function buildStaticWindow(staticDoc) {
};
}
function resolveLinkedCssPath(fileDir, href) {
const stripped = href.split(/[?#]/)[0];
const rootRelative = stripped.startsWith('/') && !stripped.startsWith('//');
if (!rootRelative) return path.resolve(fileDir, stripped);
// Drop "." and reject ".." so /../outside.css cannot walk out of dir.
const segments = stripped.replace(/^\/+/, '').split(/[/\\]/).filter(p => p && p !== '.');
if (segments.some(p => p === '..')) return path.join(fileDir, segments.filter(p => p !== '..').join(path.sep));
const rel = segments.join(path.sep);
let dir = fileDir;
for (;;) {
const parent = path.dirname(dir);
if (parent === dir) break; // never use the filesystem root as document root
try {
const candidate = path.join(dir, rel);
if (fs.statSync(candidate).isFile()) return candidate;
} catch { /* missing or unreadable candidate */ }
// Stop at the project root so a coincidental ~/static/app.css cannot win.
try {
if (fs.existsSync(path.join(dir, 'package.json')) || fs.existsSync(path.join(dir, '.git'))) break;
} catch { /* unreadable marker */ }
dir = parent;
}
return path.join(fileDir, rel);
}
function collectStaticCssText(root, fileDir, profile, filePath, modules) {
const styleTexts = [];
const warnedMissingStylesheets = new Set();
for (const styleEl of modules.selectAll('style', root.children || [])) {
styleTexts.push(modules.domutils.textContent(styleEl));
}
@@ -974,10 +1000,10 @@ function collectStaticCssText(root, fileDir, profile, filePath, modules) {
const rel = link.attribs?.rel || '';
const href = link.attribs?.href || '';
if (!/\bstylesheet\b/i.test(rel) || !href || /^(https?:)?\/\//i.test(href)) continue;
// Cache-busting hrefs (styles.css?v=3) resolve to the file, not to a
// literal path with the query in it; a versioned link otherwise made the
// whole stylesheet invisible to every element-level check.
const cssPath = path.resolve(fileDir, href.split(/[?#]/)[0]);
// Cache-busting (styles.css?v=3) and root-relative (/static/app.css) hrefs
// must not resolve as OS-absolute paths; otherwise the whole stylesheet is
// invisible to every element-level check.
const cssPath = resolveLinkedCssPath(fileDir, href);
try {
const css = profileStep(profile, {
engine: 'static-html',
@@ -987,7 +1013,14 @@ function collectStaticCssText(root, fileDir, profile, filePath, modules) {
detail: href,
}, () => fs.readFileSync(cssPath, 'utf-8'));
styleTexts.push(css);
} catch { /* skip unreadable */ }
} catch {
if (!warnedMissingStylesheets.has(cssPath)) {
warnedMissingStylesheets.add(cssPath);
process.stderr.write(
`impeccable detect: could not read linked stylesheet ${href} (resolved to ${cssPath}); color and custom-property rules will be incomplete\n`
);
}
}
}
return styleTexts.join('\n');
}
@@ -964,8 +964,34 @@ function buildStaticWindow(staticDoc) {
};
}
function resolveLinkedCssPath(fileDir, href) {
const stripped = href.split(/[?#]/)[0];
const rootRelative = stripped.startsWith('/') && !stripped.startsWith('//');
if (!rootRelative) return path.resolve(fileDir, stripped);
// Drop "." and reject ".." so /../outside.css cannot walk out of dir.
const segments = stripped.replace(/^\/+/, '').split(/[/\\]/).filter(p => p && p !== '.');
if (segments.some(p => p === '..')) return path.join(fileDir, segments.filter(p => p !== '..').join(path.sep));
const rel = segments.join(path.sep);
let dir = fileDir;
for (;;) {
const parent = path.dirname(dir);
if (parent === dir) break; // never use the filesystem root as document root
try {
const candidate = path.join(dir, rel);
if (fs.statSync(candidate).isFile()) return candidate;
} catch { /* missing or unreadable candidate */ }
// Stop at the project root so a coincidental ~/static/app.css cannot win.
try {
if (fs.existsSync(path.join(dir, 'package.json')) || fs.existsSync(path.join(dir, '.git'))) break;
} catch { /* unreadable marker */ }
dir = parent;
}
return path.join(fileDir, rel);
}
function collectStaticCssText(root, fileDir, profile, filePath, modules) {
const styleTexts = [];
const warnedMissingStylesheets = new Set();
for (const styleEl of modules.selectAll('style', root.children || [])) {
styleTexts.push(modules.domutils.textContent(styleEl));
}
@@ -974,10 +1000,10 @@ function collectStaticCssText(root, fileDir, profile, filePath, modules) {
const rel = link.attribs?.rel || '';
const href = link.attribs?.href || '';
if (!/\bstylesheet\b/i.test(rel) || !href || /^(https?:)?\/\//i.test(href)) continue;
// Cache-busting hrefs (styles.css?v=3) resolve to the file, not to a
// literal path with the query in it; a versioned link otherwise made the
// whole stylesheet invisible to every element-level check.
const cssPath = path.resolve(fileDir, href.split(/[?#]/)[0]);
// Cache-busting (styles.css?v=3) and root-relative (/static/app.css) hrefs
// must not resolve as OS-absolute paths; otherwise the whole stylesheet is
// invisible to every element-level check.
const cssPath = resolveLinkedCssPath(fileDir, href);
try {
const css = profileStep(profile, {
engine: 'static-html',
@@ -987,7 +1013,14 @@ function collectStaticCssText(root, fileDir, profile, filePath, modules) {
detail: href,
}, () => fs.readFileSync(cssPath, 'utf-8'));
styleTexts.push(css);
} catch { /* skip unreadable */ }
} catch {
if (!warnedMissingStylesheets.has(cssPath)) {
warnedMissingStylesheets.add(cssPath);
process.stderr.write(
`impeccable detect: could not read linked stylesheet ${href} (resolved to ${cssPath}); color and custom-property rules will be incomplete\n`
);
}
}
}
return styleTexts.join('\n');
}
@@ -964,8 +964,34 @@ function buildStaticWindow(staticDoc) {
};
}
function resolveLinkedCssPath(fileDir, href) {
const stripped = href.split(/[?#]/)[0];
const rootRelative = stripped.startsWith('/') && !stripped.startsWith('//');
if (!rootRelative) return path.resolve(fileDir, stripped);
// Drop "." and reject ".." so /../outside.css cannot walk out of dir.
const segments = stripped.replace(/^\/+/, '').split(/[/\\]/).filter(p => p && p !== '.');
if (segments.some(p => p === '..')) return path.join(fileDir, segments.filter(p => p !== '..').join(path.sep));
const rel = segments.join(path.sep);
let dir = fileDir;
for (;;) {
const parent = path.dirname(dir);
if (parent === dir) break; // never use the filesystem root as document root
try {
const candidate = path.join(dir, rel);
if (fs.statSync(candidate).isFile()) return candidate;
} catch { /* missing or unreadable candidate */ }
// Stop at the project root so a coincidental ~/static/app.css cannot win.
try {
if (fs.existsSync(path.join(dir, 'package.json')) || fs.existsSync(path.join(dir, '.git'))) break;
} catch { /* unreadable marker */ }
dir = parent;
}
return path.join(fileDir, rel);
}
function collectStaticCssText(root, fileDir, profile, filePath, modules) {
const styleTexts = [];
const warnedMissingStylesheets = new Set();
for (const styleEl of modules.selectAll('style', root.children || [])) {
styleTexts.push(modules.domutils.textContent(styleEl));
}
@@ -974,10 +1000,10 @@ function collectStaticCssText(root, fileDir, profile, filePath, modules) {
const rel = link.attribs?.rel || '';
const href = link.attribs?.href || '';
if (!/\bstylesheet\b/i.test(rel) || !href || /^(https?:)?\/\//i.test(href)) continue;
// Cache-busting hrefs (styles.css?v=3) resolve to the file, not to a
// literal path with the query in it; a versioned link otherwise made the
// whole stylesheet invisible to every element-level check.
const cssPath = path.resolve(fileDir, href.split(/[?#]/)[0]);
// Cache-busting (styles.css?v=3) and root-relative (/static/app.css) hrefs
// must not resolve as OS-absolute paths; otherwise the whole stylesheet is
// invisible to every element-level check.
const cssPath = resolveLinkedCssPath(fileDir, href);
try {
const css = profileStep(profile, {
engine: 'static-html',
@@ -987,7 +1013,14 @@ function collectStaticCssText(root, fileDir, profile, filePath, modules) {
detail: href,
}, () => fs.readFileSync(cssPath, 'utf-8'));
styleTexts.push(css);
} catch { /* skip unreadable */ }
} catch {
if (!warnedMissingStylesheets.has(cssPath)) {
warnedMissingStylesheets.add(cssPath);
process.stderr.write(
`impeccable detect: could not read linked stylesheet ${href} (resolved to ${cssPath}); color and custom-property rules will be incomplete\n`
);
}
}
}
return styleTexts.join('\n');
}
@@ -964,8 +964,34 @@ function buildStaticWindow(staticDoc) {
};
}
function resolveLinkedCssPath(fileDir, href) {
const stripped = href.split(/[?#]/)[0];
const rootRelative = stripped.startsWith('/') && !stripped.startsWith('//');
if (!rootRelative) return path.resolve(fileDir, stripped);
// Drop "." and reject ".." so /../outside.css cannot walk out of dir.
const segments = stripped.replace(/^\/+/, '').split(/[/\\]/).filter(p => p && p !== '.');
if (segments.some(p => p === '..')) return path.join(fileDir, segments.filter(p => p !== '..').join(path.sep));
const rel = segments.join(path.sep);
let dir = fileDir;
for (;;) {
const parent = path.dirname(dir);
if (parent === dir) break; // never use the filesystem root as document root
try {
const candidate = path.join(dir, rel);
if (fs.statSync(candidate).isFile()) return candidate;
} catch { /* missing or unreadable candidate */ }
// Stop at the project root so a coincidental ~/static/app.css cannot win.
try {
if (fs.existsSync(path.join(dir, 'package.json')) || fs.existsSync(path.join(dir, '.git'))) break;
} catch { /* unreadable marker */ }
dir = parent;
}
return path.join(fileDir, rel);
}
function collectStaticCssText(root, fileDir, profile, filePath, modules) {
const styleTexts = [];
const warnedMissingStylesheets = new Set();
for (const styleEl of modules.selectAll('style', root.children || [])) {
styleTexts.push(modules.domutils.textContent(styleEl));
}
@@ -974,10 +1000,10 @@ function collectStaticCssText(root, fileDir, profile, filePath, modules) {
const rel = link.attribs?.rel || '';
const href = link.attribs?.href || '';
if (!/\bstylesheet\b/i.test(rel) || !href || /^(https?:)?\/\//i.test(href)) continue;
// Cache-busting hrefs (styles.css?v=3) resolve to the file, not to a
// literal path with the query in it; a versioned link otherwise made the
// whole stylesheet invisible to every element-level check.
const cssPath = path.resolve(fileDir, href.split(/[?#]/)[0]);
// Cache-busting (styles.css?v=3) and root-relative (/static/app.css) hrefs
// must not resolve as OS-absolute paths; otherwise the whole stylesheet is
// invisible to every element-level check.
const cssPath = resolveLinkedCssPath(fileDir, href);
try {
const css = profileStep(profile, {
engine: 'static-html',
@@ -987,7 +1013,14 @@ function collectStaticCssText(root, fileDir, profile, filePath, modules) {
detail: href,
}, () => fs.readFileSync(cssPath, 'utf-8'));
styleTexts.push(css);
} catch { /* skip unreadable */ }
} catch {
if (!warnedMissingStylesheets.has(cssPath)) {
warnedMissingStylesheets.add(cssPath);
process.stderr.write(
`impeccable detect: could not read linked stylesheet ${href} (resolved to ${cssPath}); color and custom-property rules will be incomplete\n`
);
}
}
}
return styleTexts.join('\n');
}
@@ -964,8 +964,34 @@ function buildStaticWindow(staticDoc) {
};
}
function resolveLinkedCssPath(fileDir, href) {
const stripped = href.split(/[?#]/)[0];
const rootRelative = stripped.startsWith('/') && !stripped.startsWith('//');
if (!rootRelative) return path.resolve(fileDir, stripped);
// Drop "." and reject ".." so /../outside.css cannot walk out of dir.
const segments = stripped.replace(/^\/+/, '').split(/[/\\]/).filter(p => p && p !== '.');
if (segments.some(p => p === '..')) return path.join(fileDir, segments.filter(p => p !== '..').join(path.sep));
const rel = segments.join(path.sep);
let dir = fileDir;
for (;;) {
const parent = path.dirname(dir);
if (parent === dir) break; // never use the filesystem root as document root
try {
const candidate = path.join(dir, rel);
if (fs.statSync(candidate).isFile()) return candidate;
} catch { /* missing or unreadable candidate */ }
// Stop at the project root so a coincidental ~/static/app.css cannot win.
try {
if (fs.existsSync(path.join(dir, 'package.json')) || fs.existsSync(path.join(dir, '.git'))) break;
} catch { /* unreadable marker */ }
dir = parent;
}
return path.join(fileDir, rel);
}
function collectStaticCssText(root, fileDir, profile, filePath, modules) {
const styleTexts = [];
const warnedMissingStylesheets = new Set();
for (const styleEl of modules.selectAll('style', root.children || [])) {
styleTexts.push(modules.domutils.textContent(styleEl));
}
@@ -974,10 +1000,10 @@ function collectStaticCssText(root, fileDir, profile, filePath, modules) {
const rel = link.attribs?.rel || '';
const href = link.attribs?.href || '';
if (!/\bstylesheet\b/i.test(rel) || !href || /^(https?:)?\/\//i.test(href)) continue;
// Cache-busting hrefs (styles.css?v=3) resolve to the file, not to a
// literal path with the query in it; a versioned link otherwise made the
// whole stylesheet invisible to every element-level check.
const cssPath = path.resolve(fileDir, href.split(/[?#]/)[0]);
// Cache-busting (styles.css?v=3) and root-relative (/static/app.css) hrefs
// must not resolve as OS-absolute paths; otherwise the whole stylesheet is
// invisible to every element-level check.
const cssPath = resolveLinkedCssPath(fileDir, href);
try {
const css = profileStep(profile, {
engine: 'static-html',
@@ -987,7 +1013,14 @@ function collectStaticCssText(root, fileDir, profile, filePath, modules) {
detail: href,
}, () => fs.readFileSync(cssPath, 'utf-8'));
styleTexts.push(css);
} catch { /* skip unreadable */ }
} catch {
if (!warnedMissingStylesheets.has(cssPath)) {
warnedMissingStylesheets.add(cssPath);
process.stderr.write(
`impeccable detect: could not read linked stylesheet ${href} (resolved to ${cssPath}); color and custom-property rules will be incomplete\n`
);
}
}
}
return styleTexts.join('\n');
}
@@ -964,8 +964,34 @@ function buildStaticWindow(staticDoc) {
};
}
function resolveLinkedCssPath(fileDir, href) {
const stripped = href.split(/[?#]/)[0];
const rootRelative = stripped.startsWith('/') && !stripped.startsWith('//');
if (!rootRelative) return path.resolve(fileDir, stripped);
// Drop "." and reject ".." so /../outside.css cannot walk out of dir.
const segments = stripped.replace(/^\/+/, '').split(/[/\\]/).filter(p => p && p !== '.');
if (segments.some(p => p === '..')) return path.join(fileDir, segments.filter(p => p !== '..').join(path.sep));
const rel = segments.join(path.sep);
let dir = fileDir;
for (;;) {
const parent = path.dirname(dir);
if (parent === dir) break; // never use the filesystem root as document root
try {
const candidate = path.join(dir, rel);
if (fs.statSync(candidate).isFile()) return candidate;
} catch { /* missing or unreadable candidate */ }
// Stop at the project root so a coincidental ~/static/app.css cannot win.
try {
if (fs.existsSync(path.join(dir, 'package.json')) || fs.existsSync(path.join(dir, '.git'))) break;
} catch { /* unreadable marker */ }
dir = parent;
}
return path.join(fileDir, rel);
}
function collectStaticCssText(root, fileDir, profile, filePath, modules) {
const styleTexts = [];
const warnedMissingStylesheets = new Set();
for (const styleEl of modules.selectAll('style', root.children || [])) {
styleTexts.push(modules.domutils.textContent(styleEl));
}
@@ -974,10 +1000,10 @@ function collectStaticCssText(root, fileDir, profile, filePath, modules) {
const rel = link.attribs?.rel || '';
const href = link.attribs?.href || '';
if (!/\bstylesheet\b/i.test(rel) || !href || /^(https?:)?\/\//i.test(href)) continue;
// Cache-busting hrefs (styles.css?v=3) resolve to the file, not to a
// literal path with the query in it; a versioned link otherwise made the
// whole stylesheet invisible to every element-level check.
const cssPath = path.resolve(fileDir, href.split(/[?#]/)[0]);
// Cache-busting (styles.css?v=3) and root-relative (/static/app.css) hrefs
// must not resolve as OS-absolute paths; otherwise the whole stylesheet is
// invisible to every element-level check.
const cssPath = resolveLinkedCssPath(fileDir, href);
try {
const css = profileStep(profile, {
engine: 'static-html',
@@ -987,7 +1013,14 @@ function collectStaticCssText(root, fileDir, profile, filePath, modules) {
detail: href,
}, () => fs.readFileSync(cssPath, 'utf-8'));
styleTexts.push(css);
} catch { /* skip unreadable */ }
} catch {
if (!warnedMissingStylesheets.has(cssPath)) {
warnedMissingStylesheets.add(cssPath);
process.stderr.write(
`impeccable detect: could not read linked stylesheet ${href} (resolved to ${cssPath}); color and custom-property rules will be incomplete\n`
);
}
}
}
return styleTexts.join('\n');
}
@@ -964,8 +964,34 @@ function buildStaticWindow(staticDoc) {
};
}
function resolveLinkedCssPath(fileDir, href) {
const stripped = href.split(/[?#]/)[0];
const rootRelative = stripped.startsWith('/') && !stripped.startsWith('//');
if (!rootRelative) return path.resolve(fileDir, stripped);
// Drop "." and reject ".." so /../outside.css cannot walk out of dir.
const segments = stripped.replace(/^\/+/, '').split(/[/\\]/).filter(p => p && p !== '.');
if (segments.some(p => p === '..')) return path.join(fileDir, segments.filter(p => p !== '..').join(path.sep));
const rel = segments.join(path.sep);
let dir = fileDir;
for (;;) {
const parent = path.dirname(dir);
if (parent === dir) break; // never use the filesystem root as document root
try {
const candidate = path.join(dir, rel);
if (fs.statSync(candidate).isFile()) return candidate;
} catch { /* missing or unreadable candidate */ }
// Stop at the project root so a coincidental ~/static/app.css cannot win.
try {
if (fs.existsSync(path.join(dir, 'package.json')) || fs.existsSync(path.join(dir, '.git'))) break;
} catch { /* unreadable marker */ }
dir = parent;
}
return path.join(fileDir, rel);
}
function collectStaticCssText(root, fileDir, profile, filePath, modules) {
const styleTexts = [];
const warnedMissingStylesheets = new Set();
for (const styleEl of modules.selectAll('style', root.children || [])) {
styleTexts.push(modules.domutils.textContent(styleEl));
}
@@ -974,10 +1000,10 @@ function collectStaticCssText(root, fileDir, profile, filePath, modules) {
const rel = link.attribs?.rel || '';
const href = link.attribs?.href || '';
if (!/\bstylesheet\b/i.test(rel) || !href || /^(https?:)?\/\//i.test(href)) continue;
// Cache-busting hrefs (styles.css?v=3) resolve to the file, not to a
// literal path with the query in it; a versioned link otherwise made the
// whole stylesheet invisible to every element-level check.
const cssPath = path.resolve(fileDir, href.split(/[?#]/)[0]);
// Cache-busting (styles.css?v=3) and root-relative (/static/app.css) hrefs
// must not resolve as OS-absolute paths; otherwise the whole stylesheet is
// invisible to every element-level check.
const cssPath = resolveLinkedCssPath(fileDir, href);
try {
const css = profileStep(profile, {
engine: 'static-html',
@@ -987,7 +1013,14 @@ function collectStaticCssText(root, fileDir, profile, filePath, modules) {
detail: href,
}, () => fs.readFileSync(cssPath, 'utf-8'));
styleTexts.push(css);
} catch { /* skip unreadable */ }
} catch {
if (!warnedMissingStylesheets.has(cssPath)) {
warnedMissingStylesheets.add(cssPath);
process.stderr.write(
`impeccable detect: could not read linked stylesheet ${href} (resolved to ${cssPath}); color and custom-property rules will be incomplete\n`
);
}
}
}
return styleTexts.join('\n');
}
@@ -964,8 +964,34 @@ function buildStaticWindow(staticDoc) {
};
}
function resolveLinkedCssPath(fileDir, href) {
const stripped = href.split(/[?#]/)[0];
const rootRelative = stripped.startsWith('/') && !stripped.startsWith('//');
if (!rootRelative) return path.resolve(fileDir, stripped);
// Drop "." and reject ".." so /../outside.css cannot walk out of dir.
const segments = stripped.replace(/^\/+/, '').split(/[/\\]/).filter(p => p && p !== '.');
if (segments.some(p => p === '..')) return path.join(fileDir, segments.filter(p => p !== '..').join(path.sep));
const rel = segments.join(path.sep);
let dir = fileDir;
for (;;) {
const parent = path.dirname(dir);
if (parent === dir) break; // never use the filesystem root as document root
try {
const candidate = path.join(dir, rel);
if (fs.statSync(candidate).isFile()) return candidate;
} catch { /* missing or unreadable candidate */ }
// Stop at the project root so a coincidental ~/static/app.css cannot win.
try {
if (fs.existsSync(path.join(dir, 'package.json')) || fs.existsSync(path.join(dir, '.git'))) break;
} catch { /* unreadable marker */ }
dir = parent;
}
return path.join(fileDir, rel);
}
function collectStaticCssText(root, fileDir, profile, filePath, modules) {
const styleTexts = [];
const warnedMissingStylesheets = new Set();
for (const styleEl of modules.selectAll('style', root.children || [])) {
styleTexts.push(modules.domutils.textContent(styleEl));
}
@@ -974,10 +1000,10 @@ function collectStaticCssText(root, fileDir, profile, filePath, modules) {
const rel = link.attribs?.rel || '';
const href = link.attribs?.href || '';
if (!/\bstylesheet\b/i.test(rel) || !href || /^(https?:)?\/\//i.test(href)) continue;
// Cache-busting hrefs (styles.css?v=3) resolve to the file, not to a
// literal path with the query in it; a versioned link otherwise made the
// whole stylesheet invisible to every element-level check.
const cssPath = path.resolve(fileDir, href.split(/[?#]/)[0]);
// Cache-busting (styles.css?v=3) and root-relative (/static/app.css) hrefs
// must not resolve as OS-absolute paths; otherwise the whole stylesheet is
// invisible to every element-level check.
const cssPath = resolveLinkedCssPath(fileDir, href);
try {
const css = profileStep(profile, {
engine: 'static-html',
@@ -987,7 +1013,14 @@ function collectStaticCssText(root, fileDir, profile, filePath, modules) {
detail: href,
}, () => fs.readFileSync(cssPath, 'utf-8'));
styleTexts.push(css);
} catch { /* skip unreadable */ }
} catch {
if (!warnedMissingStylesheets.has(cssPath)) {
warnedMissingStylesheets.add(cssPath);
process.stderr.write(
`impeccable detect: could not read linked stylesheet ${href} (resolved to ${cssPath}); color and custom-property rules will be incomplete\n`
);
}
}
}
return styleTexts.join('\n');
}
@@ -964,8 +964,34 @@ function buildStaticWindow(staticDoc) {
};
}
function resolveLinkedCssPath(fileDir, href) {
const stripped = href.split(/[?#]/)[0];
const rootRelative = stripped.startsWith('/') && !stripped.startsWith('//');
if (!rootRelative) return path.resolve(fileDir, stripped);
// Drop "." and reject ".." so /../outside.css cannot walk out of dir.
const segments = stripped.replace(/^\/+/, '').split(/[/\\]/).filter(p => p && p !== '.');
if (segments.some(p => p === '..')) return path.join(fileDir, segments.filter(p => p !== '..').join(path.sep));
const rel = segments.join(path.sep);
let dir = fileDir;
for (;;) {
const parent = path.dirname(dir);
if (parent === dir) break; // never use the filesystem root as document root
try {
const candidate = path.join(dir, rel);
if (fs.statSync(candidate).isFile()) return candidate;
} catch { /* missing or unreadable candidate */ }
// Stop at the project root so a coincidental ~/static/app.css cannot win.
try {
if (fs.existsSync(path.join(dir, 'package.json')) || fs.existsSync(path.join(dir, '.git'))) break;
} catch { /* unreadable marker */ }
dir = parent;
}
return path.join(fileDir, rel);
}
function collectStaticCssText(root, fileDir, profile, filePath, modules) {
const styleTexts = [];
const warnedMissingStylesheets = new Set();
for (const styleEl of modules.selectAll('style', root.children || [])) {
styleTexts.push(modules.domutils.textContent(styleEl));
}
@@ -974,10 +1000,10 @@ function collectStaticCssText(root, fileDir, profile, filePath, modules) {
const rel = link.attribs?.rel || '';
const href = link.attribs?.href || '';
if (!/\bstylesheet\b/i.test(rel) || !href || /^(https?:)?\/\//i.test(href)) continue;
// Cache-busting hrefs (styles.css?v=3) resolve to the file, not to a
// literal path with the query in it; a versioned link otherwise made the
// whole stylesheet invisible to every element-level check.
const cssPath = path.resolve(fileDir, href.split(/[?#]/)[0]);
// Cache-busting (styles.css?v=3) and root-relative (/static/app.css) hrefs
// must not resolve as OS-absolute paths; otherwise the whole stylesheet is
// invisible to every element-level check.
const cssPath = resolveLinkedCssPath(fileDir, href);
try {
const css = profileStep(profile, {
engine: 'static-html',
@@ -987,7 +1013,14 @@ function collectStaticCssText(root, fileDir, profile, filePath, modules) {
detail: href,
}, () => fs.readFileSync(cssPath, 'utf-8'));
styleTexts.push(css);
} catch { /* skip unreadable */ }
} catch {
if (!warnedMissingStylesheets.has(cssPath)) {
warnedMissingStylesheets.add(cssPath);
process.stderr.write(
`impeccable detect: could not read linked stylesheet ${href} (resolved to ${cssPath}); color and custom-property rules will be incomplete\n`
);
}
}
}
return styleTexts.join('\n');
}
@@ -964,8 +964,34 @@ function buildStaticWindow(staticDoc) {
};
}
function resolveLinkedCssPath(fileDir, href) {
const stripped = href.split(/[?#]/)[0];
const rootRelative = stripped.startsWith('/') && !stripped.startsWith('//');
if (!rootRelative) return path.resolve(fileDir, stripped);
// Drop "." and reject ".." so /../outside.css cannot walk out of dir.
const segments = stripped.replace(/^\/+/, '').split(/[/\\]/).filter(p => p && p !== '.');
if (segments.some(p => p === '..')) return path.join(fileDir, segments.filter(p => p !== '..').join(path.sep));
const rel = segments.join(path.sep);
let dir = fileDir;
for (;;) {
const parent = path.dirname(dir);
if (parent === dir) break; // never use the filesystem root as document root
try {
const candidate = path.join(dir, rel);
if (fs.statSync(candidate).isFile()) return candidate;
} catch { /* missing or unreadable candidate */ }
// Stop at the project root so a coincidental ~/static/app.css cannot win.
try {
if (fs.existsSync(path.join(dir, 'package.json')) || fs.existsSync(path.join(dir, '.git'))) break;
} catch { /* unreadable marker */ }
dir = parent;
}
return path.join(fileDir, rel);
}
function collectStaticCssText(root, fileDir, profile, filePath, modules) {
const styleTexts = [];
const warnedMissingStylesheets = new Set();
for (const styleEl of modules.selectAll('style', root.children || [])) {
styleTexts.push(modules.domutils.textContent(styleEl));
}
@@ -974,10 +1000,10 @@ function collectStaticCssText(root, fileDir, profile, filePath, modules) {
const rel = link.attribs?.rel || '';
const href = link.attribs?.href || '';
if (!/\bstylesheet\b/i.test(rel) || !href || /^(https?:)?\/\//i.test(href)) continue;
// Cache-busting hrefs (styles.css?v=3) resolve to the file, not to a
// literal path with the query in it; a versioned link otherwise made the
// whole stylesheet invisible to every element-level check.
const cssPath = path.resolve(fileDir, href.split(/[?#]/)[0]);
// Cache-busting (styles.css?v=3) and root-relative (/static/app.css) hrefs
// must not resolve as OS-absolute paths; otherwise the whole stylesheet is
// invisible to every element-level check.
const cssPath = resolveLinkedCssPath(fileDir, href);
try {
const css = profileStep(profile, {
engine: 'static-html',
@@ -987,7 +1013,14 @@ function collectStaticCssText(root, fileDir, profile, filePath, modules) {
detail: href,
}, () => fs.readFileSync(cssPath, 'utf-8'));
styleTexts.push(css);
} catch { /* skip unreadable */ }
} catch {
if (!warnedMissingStylesheets.has(cssPath)) {
warnedMissingStylesheets.add(cssPath);
process.stderr.write(
`impeccable detect: could not read linked stylesheet ${href} (resolved to ${cssPath}); color and custom-property rules will be incomplete\n`
);
}
}
}
return styleTexts.join('\n');
}
@@ -964,8 +964,34 @@ function buildStaticWindow(staticDoc) {
};
}
function resolveLinkedCssPath(fileDir, href) {
const stripped = href.split(/[?#]/)[0];
const rootRelative = stripped.startsWith('/') && !stripped.startsWith('//');
if (!rootRelative) return path.resolve(fileDir, stripped);
// Drop "." and reject ".." so /../outside.css cannot walk out of dir.
const segments = stripped.replace(/^\/+/, '').split(/[/\\]/).filter(p => p && p !== '.');
if (segments.some(p => p === '..')) return path.join(fileDir, segments.filter(p => p !== '..').join(path.sep));
const rel = segments.join(path.sep);
let dir = fileDir;
for (;;) {
const parent = path.dirname(dir);
if (parent === dir) break; // never use the filesystem root as document root
try {
const candidate = path.join(dir, rel);
if (fs.statSync(candidate).isFile()) return candidate;
} catch { /* missing or unreadable candidate */ }
// Stop at the project root so a coincidental ~/static/app.css cannot win.
try {
if (fs.existsSync(path.join(dir, 'package.json')) || fs.existsSync(path.join(dir, '.git'))) break;
} catch { /* unreadable marker */ }
dir = parent;
}
return path.join(fileDir, rel);
}
function collectStaticCssText(root, fileDir, profile, filePath, modules) {
const styleTexts = [];
const warnedMissingStylesheets = new Set();
for (const styleEl of modules.selectAll('style', root.children || [])) {
styleTexts.push(modules.domutils.textContent(styleEl));
}
@@ -974,10 +1000,10 @@ function collectStaticCssText(root, fileDir, profile, filePath, modules) {
const rel = link.attribs?.rel || '';
const href = link.attribs?.href || '';
if (!/\bstylesheet\b/i.test(rel) || !href || /^(https?:)?\/\//i.test(href)) continue;
// Cache-busting hrefs (styles.css?v=3) resolve to the file, not to a
// literal path with the query in it; a versioned link otherwise made the
// whole stylesheet invisible to every element-level check.
const cssPath = path.resolve(fileDir, href.split(/[?#]/)[0]);
// Cache-busting (styles.css?v=3) and root-relative (/static/app.css) hrefs
// must not resolve as OS-absolute paths; otherwise the whole stylesheet is
// invisible to every element-level check.
const cssPath = resolveLinkedCssPath(fileDir, href);
try {
const css = profileStep(profile, {
engine: 'static-html',
@@ -987,7 +1013,14 @@ function collectStaticCssText(root, fileDir, profile, filePath, modules) {
detail: href,
}, () => fs.readFileSync(cssPath, 'utf-8'));
styleTexts.push(css);
} catch { /* skip unreadable */ }
} catch {
if (!warnedMissingStylesheets.has(cssPath)) {
warnedMissingStylesheets.add(cssPath);
process.stderr.write(
`impeccable detect: could not read linked stylesheet ${href} (resolved to ${cssPath}); color and custom-property rules will be incomplete\n`
);
}
}
}
return styleTexts.join('\n');
}
@@ -964,8 +964,34 @@ function buildStaticWindow(staticDoc) {
};
}
function resolveLinkedCssPath(fileDir, href) {
const stripped = href.split(/[?#]/)[0];
const rootRelative = stripped.startsWith('/') && !stripped.startsWith('//');
if (!rootRelative) return path.resolve(fileDir, stripped);
// Drop "." and reject ".." so /../outside.css cannot walk out of dir.
const segments = stripped.replace(/^\/+/, '').split(/[/\\]/).filter(p => p && p !== '.');
if (segments.some(p => p === '..')) return path.join(fileDir, segments.filter(p => p !== '..').join(path.sep));
const rel = segments.join(path.sep);
let dir = fileDir;
for (;;) {
const parent = path.dirname(dir);
if (parent === dir) break; // never use the filesystem root as document root
try {
const candidate = path.join(dir, rel);
if (fs.statSync(candidate).isFile()) return candidate;
} catch { /* missing or unreadable candidate */ }
// Stop at the project root so a coincidental ~/static/app.css cannot win.
try {
if (fs.existsSync(path.join(dir, 'package.json')) || fs.existsSync(path.join(dir, '.git'))) break;
} catch { /* unreadable marker */ }
dir = parent;
}
return path.join(fileDir, rel);
}
function collectStaticCssText(root, fileDir, profile, filePath, modules) {
const styleTexts = [];
const warnedMissingStylesheets = new Set();
for (const styleEl of modules.selectAll('style', root.children || [])) {
styleTexts.push(modules.domutils.textContent(styleEl));
}
@@ -974,10 +1000,10 @@ function collectStaticCssText(root, fileDir, profile, filePath, modules) {
const rel = link.attribs?.rel || '';
const href = link.attribs?.href || '';
if (!/\bstylesheet\b/i.test(rel) || !href || /^(https?:)?\/\//i.test(href)) continue;
// Cache-busting hrefs (styles.css?v=3) resolve to the file, not to a
// literal path with the query in it; a versioned link otherwise made the
// whole stylesheet invisible to every element-level check.
const cssPath = path.resolve(fileDir, href.split(/[?#]/)[0]);
// Cache-busting (styles.css?v=3) and root-relative (/static/app.css) hrefs
// must not resolve as OS-absolute paths; otherwise the whole stylesheet is
// invisible to every element-level check.
const cssPath = resolveLinkedCssPath(fileDir, href);
try {
const css = profileStep(profile, {
engine: 'static-html',
@@ -987,7 +1013,14 @@ function collectStaticCssText(root, fileDir, profile, filePath, modules) {
detail: href,
}, () => fs.readFileSync(cssPath, 'utf-8'));
styleTexts.push(css);
} catch { /* skip unreadable */ }
} catch {
if (!warnedMissingStylesheets.has(cssPath)) {
warnedMissingStylesheets.add(cssPath);
process.stderr.write(
`impeccable detect: could not read linked stylesheet ${href} (resolved to ${cssPath}); color and custom-property rules will be incomplete\n`
);
}
}
}
return styleTexts.join('\n');
}
@@ -964,8 +964,34 @@ function buildStaticWindow(staticDoc) {
};
}
function resolveLinkedCssPath(fileDir, href) {
const stripped = href.split(/[?#]/)[0];
const rootRelative = stripped.startsWith('/') && !stripped.startsWith('//');
if (!rootRelative) return path.resolve(fileDir, stripped);
// Drop "." and reject ".." so /../outside.css cannot walk out of dir.
const segments = stripped.replace(/^\/+/, '').split(/[/\\]/).filter(p => p && p !== '.');
if (segments.some(p => p === '..')) return path.join(fileDir, segments.filter(p => p !== '..').join(path.sep));
const rel = segments.join(path.sep);
let dir = fileDir;
for (;;) {
const parent = path.dirname(dir);
if (parent === dir) break; // never use the filesystem root as document root
try {
const candidate = path.join(dir, rel);
if (fs.statSync(candidate).isFile()) return candidate;
} catch { /* missing or unreadable candidate */ }
// Stop at the project root so a coincidental ~/static/app.css cannot win.
try {
if (fs.existsSync(path.join(dir, 'package.json')) || fs.existsSync(path.join(dir, '.git'))) break;
} catch { /* unreadable marker */ }
dir = parent;
}
return path.join(fileDir, rel);
}
function collectStaticCssText(root, fileDir, profile, filePath, modules) {
const styleTexts = [];
const warnedMissingStylesheets = new Set();
for (const styleEl of modules.selectAll('style', root.children || [])) {
styleTexts.push(modules.domutils.textContent(styleEl));
}
@@ -974,10 +1000,10 @@ function collectStaticCssText(root, fileDir, profile, filePath, modules) {
const rel = link.attribs?.rel || '';
const href = link.attribs?.href || '';
if (!/\bstylesheet\b/i.test(rel) || !href || /^(https?:)?\/\//i.test(href)) continue;
// Cache-busting hrefs (styles.css?v=3) resolve to the file, not to a
// literal path with the query in it; a versioned link otherwise made the
// whole stylesheet invisible to every element-level check.
const cssPath = path.resolve(fileDir, href.split(/[?#]/)[0]);
// Cache-busting (styles.css?v=3) and root-relative (/static/app.css) hrefs
// must not resolve as OS-absolute paths; otherwise the whole stylesheet is
// invisible to every element-level check.
const cssPath = resolveLinkedCssPath(fileDir, href);
try {
const css = profileStep(profile, {
engine: 'static-html',
@@ -987,7 +1013,14 @@ function collectStaticCssText(root, fileDir, profile, filePath, modules) {
detail: href,
}, () => fs.readFileSync(cssPath, 'utf-8'));
styleTexts.push(css);
} catch { /* skip unreadable */ }
} catch {
if (!warnedMissingStylesheets.has(cssPath)) {
warnedMissingStylesheets.add(cssPath);
process.stderr.write(
`impeccable detect: could not read linked stylesheet ${href} (resolved to ${cssPath}); color and custom-property rules will be incomplete\n`
);
}
}
}
return styleTexts.join('\n');
}
@@ -964,8 +964,34 @@ function buildStaticWindow(staticDoc) {
};
}
function resolveLinkedCssPath(fileDir, href) {
const stripped = href.split(/[?#]/)[0];
const rootRelative = stripped.startsWith('/') && !stripped.startsWith('//');
if (!rootRelative) return path.resolve(fileDir, stripped);
// Drop "." and reject ".." so /../outside.css cannot walk out of dir.
const segments = stripped.replace(/^\/+/, '').split(/[/\\]/).filter(p => p && p !== '.');
if (segments.some(p => p === '..')) return path.join(fileDir, segments.filter(p => p !== '..').join(path.sep));
const rel = segments.join(path.sep);
let dir = fileDir;
for (;;) {
const parent = path.dirname(dir);
if (parent === dir) break; // never use the filesystem root as document root
try {
const candidate = path.join(dir, rel);
if (fs.statSync(candidate).isFile()) return candidate;
} catch { /* missing or unreadable candidate */ }
// Stop at the project root so a coincidental ~/static/app.css cannot win.
try {
if (fs.existsSync(path.join(dir, 'package.json')) || fs.existsSync(path.join(dir, '.git'))) break;
} catch { /* unreadable marker */ }
dir = parent;
}
return path.join(fileDir, rel);
}
function collectStaticCssText(root, fileDir, profile, filePath, modules) {
const styleTexts = [];
const warnedMissingStylesheets = new Set();
for (const styleEl of modules.selectAll('style', root.children || [])) {
styleTexts.push(modules.domutils.textContent(styleEl));
}
@@ -974,10 +1000,10 @@ function collectStaticCssText(root, fileDir, profile, filePath, modules) {
const rel = link.attribs?.rel || '';
const href = link.attribs?.href || '';
if (!/\bstylesheet\b/i.test(rel) || !href || /^(https?:)?\/\//i.test(href)) continue;
// Cache-busting hrefs (styles.css?v=3) resolve to the file, not to a
// literal path with the query in it; a versioned link otherwise made the
// whole stylesheet invisible to every element-level check.
const cssPath = path.resolve(fileDir, href.split(/[?#]/)[0]);
// Cache-busting (styles.css?v=3) and root-relative (/static/app.css) hrefs
// must not resolve as OS-absolute paths; otherwise the whole stylesheet is
// invisible to every element-level check.
const cssPath = resolveLinkedCssPath(fileDir, href);
try {
const css = profileStep(profile, {
engine: 'static-html',
@@ -987,7 +1013,14 @@ function collectStaticCssText(root, fileDir, profile, filePath, modules) {
detail: href,
}, () => fs.readFileSync(cssPath, 'utf-8'));
styleTexts.push(css);
} catch { /* skip unreadable */ }
} catch {
if (!warnedMissingStylesheets.has(cssPath)) {
warnedMissingStylesheets.add(cssPath);
process.stderr.write(
`impeccable detect: could not read linked stylesheet ${href} (resolved to ${cssPath}); color and custom-property rules will be incomplete\n`
);
}
}
}
return styleTexts.join('\n');
}