mirror of
https://gitee.com/beijing_hongye_huicheng/lilishop-uniapp.git
synced 2026-06-21 17:30:13 +08:00
Fix H5 all-in-one asset paths
This commit is contained in:
143
static-asset-url.contract.test.mjs
Normal file
143
static-asset-url.contract.test.mjs
Normal file
@@ -0,0 +1,143 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import { readdirSync, readFileSync, statSync } from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import test from 'node:test';
|
||||
import valueParser from 'postcss-value-parser';
|
||||
import vueTemplateCompiler from 'vue-template-compiler';
|
||||
|
||||
const roots = ['pages', 'components'];
|
||||
const extraFiles = ['App.vue', 'uni.scss'];
|
||||
const styleExtensions = new Set(['.css', '.scss']);
|
||||
|
||||
function listFiles(dir) {
|
||||
const result = [];
|
||||
for (const entry of readdirSync(dir)) {
|
||||
const fullPath = path.join(dir, entry);
|
||||
const stat = statSync(fullPath);
|
||||
if (stat.isDirectory()) {
|
||||
result.push(...listFiles(fullPath));
|
||||
} else {
|
||||
result.push(fullPath);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function stripCssCommentsPreserveOffsets(source) {
|
||||
let output = '';
|
||||
let quote = null;
|
||||
|
||||
for (let i = 0; i < source.length; i += 1) {
|
||||
const char = source[i];
|
||||
const next = source[i + 1];
|
||||
|
||||
if (quote) {
|
||||
output += char;
|
||||
if (char === '\\') {
|
||||
i += 1;
|
||||
output += source[i] || '';
|
||||
} else if (char === quote) {
|
||||
quote = null;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (char === '"' || char === "'") {
|
||||
quote = char;
|
||||
output += char;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (char === '/' && next === '*') {
|
||||
output += ' ';
|
||||
i += 2;
|
||||
while (i < source.length && !(source[i] === '*' && source[i + 1] === '/')) {
|
||||
output += source[i] === '\n' ? '\n' : ' ';
|
||||
i += 1;
|
||||
}
|
||||
output += ' ';
|
||||
i += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (char === '/' && next === '/') {
|
||||
output += ' ';
|
||||
i += 2;
|
||||
while (i < source.length && source[i] !== '\n') {
|
||||
output += ' ';
|
||||
i += 1;
|
||||
}
|
||||
if (source[i] === '\n') {
|
||||
output += '\n';
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
output += char;
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
function lineForOffset(source, offset) {
|
||||
return source.slice(0, offset).split('\n').length;
|
||||
}
|
||||
|
||||
function collectRootStaticStyleUrls(filePath, styleSource, sourceOffset = 0, originalSource = styleSource) {
|
||||
const stripped = stripCssCommentsPreserveOffsets(styleSource);
|
||||
const findings = [];
|
||||
|
||||
valueParser(stripped).walk((node) => {
|
||||
if (node.type !== 'function' || node.value.toLowerCase() !== 'url') {
|
||||
return;
|
||||
}
|
||||
|
||||
const url = valueParser.stringify(node.nodes).trim().replace(/^['"]|['"]$/g, '');
|
||||
if (!url.startsWith('/static/')) {
|
||||
return;
|
||||
}
|
||||
|
||||
findings.push({
|
||||
filePath,
|
||||
line: lineForOffset(originalSource, sourceOffset + node.sourceIndex),
|
||||
url,
|
||||
});
|
||||
});
|
||||
|
||||
return findings;
|
||||
}
|
||||
|
||||
function styleSourcesForFile(filePath) {
|
||||
const source = readFileSync(filePath, 'utf8');
|
||||
if (filePath.endsWith('.vue')) {
|
||||
const descriptor = vueTemplateCompiler.parseComponent(source);
|
||||
return descriptor.styles.map((style) => ({
|
||||
source: style.content,
|
||||
sourceOffset: style.start,
|
||||
originalSource: source,
|
||||
}));
|
||||
}
|
||||
|
||||
return [{ source, sourceOffset: 0, originalSource: source }];
|
||||
}
|
||||
|
||||
test('style url() references do not use H5-root /static paths', () => {
|
||||
const files = [
|
||||
...roots.flatMap((root) => listFiles(root)),
|
||||
...extraFiles,
|
||||
].filter((filePath) => filePath.endsWith('.vue') || styleExtensions.has(path.extname(filePath)));
|
||||
|
||||
const findings = files.flatMap((filePath) =>
|
||||
styleSourcesForFile(filePath).flatMap(({ source, sourceOffset, originalSource }) =>
|
||||
collectRootStaticStyleUrls(filePath, source, sourceOffset, originalSource)
|
||||
)
|
||||
);
|
||||
|
||||
assert.deepEqual(
|
||||
findings,
|
||||
[],
|
||||
`style url() must use relative static paths so H5 /h5 deployments do not request site-root assets:\n${
|
||||
findings.map((item) => `${item.filePath}:${item.line} ${item.url}`).join('\n')
|
||||
}`
|
||||
);
|
||||
});
|
||||
Reference in New Issue
Block a user