Skip to content

Commit

Permalink
Adds support for html base tag (#568)
Browse files Browse the repository at this point in the history
* adds support for html base tag (#566)

* cleanup
  • Loading branch information
bezoerb committed Sep 14, 2023
1 parent 93382b1 commit d498bc3
Show file tree
Hide file tree
Showing 7 changed files with 92 additions and 17 deletions.
41 changes: 26 additions & 15 deletions src/file.js
Expand Up @@ -8,7 +8,7 @@ import {promisify} from 'node:util';
import parseCssUrls from 'css-url-parser';
import {dataUriToBuffer} from 'data-uri-to-buffer';
import debugBase from 'debug';
import {findUp} from 'find-up';
import {findUpMultiple} from 'find-up';
import {globby} from 'globby';
import got from 'got';
import isGlob from 'is-glob';
Expand Down Expand Up @@ -62,7 +62,7 @@ export function normalizePath(str) {
* @returns {boolean} True if the path is remote
*/
export function isRemote(href) {
return !Buffer.isBuffer(href) && /(^\/\/)|(:\/\/)/.test(href) && !href.startsWith('file:');
return typeof href === 'string' && /(^\/\/)|(:\/\/)/.test(href) && !href.startsWith('file:');
}

/**
Expand All @@ -88,8 +88,8 @@ export function urlParse(str = '') {
* @returns {string} file uri
*/
function getFileUri(file) {
if (!isAbsolute) {
throw new Error('Path must be absolute to compute file uri');
if (!isAbsolute(file)) {
throw new Error('Path must be absolute to compute file uri. Received: ' + file);
}

const fileUrl = process.platform === 'win32' ? new URL(`file:///${file}`) : new URL(`file://${file}`);
Expand Down Expand Up @@ -117,8 +117,12 @@ export function urlResolve(from = '', to = '') {
return path.join(from.replace(/[^/]+$/, ''), to);
}

function isAbsolute(href) {
return !Buffer.isBuffer(href) && path.isAbsolute(href);
function isFilePath(href) {
return typeof href === 'string' && !isRemote(href);
}

export function isAbsolute(href) {
return isFilePath(href) && path.isAbsolute(href);
}

/**
Expand All @@ -127,7 +131,7 @@ function isAbsolute(href) {
* @returns {boolean} True if the path is relative
*/
function isRelative(href) {
return !Buffer.isBuffer(href) && !isRemote(href) && !isAbsolute(href);
return isFilePath(href) && !isAbsolute(href);
}

/**
Expand Down Expand Up @@ -522,6 +526,7 @@ export async function getDocumentPath(file, options = {}) {
if (file.stylesheets) {
const relativeRefs = file.stylesheets.filter((href) => isRelative(href));
const absoluteRefs = file.stylesheets.filter((href) => isAbsolute(href));

// If we have no stylesheets inside, fall back to path relative to process cwd
if (relativeRefs.length === 0 && absoluteRefs.length === 0) {
process.stderr.write(BASE_WARNING);
Expand Down Expand Up @@ -681,6 +686,8 @@ export async function getAssetPaths(document, file, options = {}, strict = true)
return [];
}

// consider base tag in document
const baseTagHref = document?.contents?.toString()?.match(/<base\s+href=['"]([^'"]+)['"]/)?.[1];
// Remove double dots in the middle
const normalized = path.join(file);
// Count directory hops
Expand All @@ -693,6 +700,8 @@ export async function getAssetPaths(document, file, options = {}, strict = true)
const paths = [
...new Set([
base,
baseTagHref,
baseTagHref && !isRemote(baseTagHref) && path.join(base, baseTagHref),
base && isRelative(base) && path.join(process.cwd(), base),
docurl,
urlPath && urlResolve(urlObj.href, path.dirname(urlPath)),
Expand All @@ -713,7 +722,7 @@ export async function getAssetPaths(document, file, options = {}, strict = true)

// Filter non-existent paths
const filtered = await filterAsync(paths, (f) => {
if (!f) {
if (!f || (isAbsolute(f) && !f?.includes(process.cwd()))) {
return false;
}

Expand All @@ -726,20 +735,22 @@ export async function getAssetPaths(document, file, options = {}, strict = true)
return [...result, cwd];
}

const up = await findUp(first, {cwd, type: 'directory'});
if (up) {
const upDir = path.dirname(up);
// const up = await findUp(first, {cwd, type: 'directory'});
const up = await findUpMultiple(first, {cwd, type: 'directory', stopAt: process.cwd()});
const additionalDirectories = up.flatMap((u) => {
const upDir = path.dirname(u);

if (hops) {
// Add additional directories based on dirHops
const additional = path.relative(upDir, cwd).split(path.sep).slice(0, hops);
return [...result, upDir, path.join(upDir, ...additional)];

return [upDir, path.join(upDir, ...additional)];
}

return [...result, upDir];
}
return [upDir];
});

return result;
return [...result, ...additionalDirectories];
});

debug(`(getAssetPaths) Search file "${file}" in:`, [...new Set(all)]);
Expand Down
34 changes: 34 additions & 0 deletions test/blackbox.test.js
Expand Up @@ -951,6 +951,23 @@ describe('generate (local)', () => {
assertCritical(target, '', done)
);
});

test('issue #566 - consider base tag', (done) => {
const expected = read('expected/issue-566.css');
const target = path.join(__dirname, '.issue-566.css');

generate(
{
base: FIXTURES_DIR,
src: 'issue-566.html',
target,
inline: false,
width: 1300,
height: 900,
},
assertCritical(target, expected, done)
);
});
});

describe('generate (remote)', () => {
Expand Down Expand Up @@ -1454,4 +1471,21 @@ describe('generate (remote)', () => {

expect(mockHead).toHaveBeenCalled();
});

test('issue #566 - consider base tag', (done) => {
const expected = read('expected/issue-566.css');
const target = path.join(__dirname, '.issue-566.css');

generate(
{
base: FIXTURES_DIR,
src: `http://localhost:${port}/issue-566.html`,
target,
inline: false,
width: 1300,
height: 900,
},
assertCritical(target, expected, done)
);
});
});
2 changes: 0 additions & 2 deletions test/cli.test.js
Expand Up @@ -38,12 +38,10 @@ const getArgs = async (params = []) => {
await import('../cli.js');
process.argv = origArgv;
const {generate} = await import('../index.js');
// console.log('GENERATE:', generate.mock);
const [args] = generate.mock.calls;
const [opts] = args || [{}];
expect(generate).toHaveBeenCalledTimes(1);
return opts || {};
// return {};
};

const pipe = async (filename, args = []) => {
Expand Down
1 change: 1 addition & 0 deletions test/expected/issue-566.css
@@ -0,0 +1 @@
#element{color:#0ff}
13 changes: 13 additions & 0 deletions test/file.test.js
Expand Up @@ -16,6 +16,7 @@ import {FileNotFoundError} from '../src/errors.js';
import {
BASE_WARNING,
isRemote,
isAbsolute,
checkCssOption,
fileExists,
joinPath,
Expand Down Expand Up @@ -88,6 +89,18 @@ test('Remote file detection', () => {
remote.forEach((p) => expect(isRemote(p)).toBe(true));
});

test('Absolute file detection', () => {
const invalid = ['', false, {}];
const absolute = ['/usr/tmp/bar'];
const relative = ['../test/foo.html', './usr/tmp/bar'];
const remote = ['https://test.io/', '//test.io/styles/main.css'];

invalid.forEach((p) => expect(isAbsolute(p)).toBe(false));
relative.forEach((p) => expect(isAbsolute(p)).toBe(false));
remote.forEach((p) => expect(isAbsolute(p)).toBe(false));
absolute.forEach((p) => expect(isAbsolute(p)).toBe(true));
});

test('Error for file not found', () => {
expect(vinylize({filenpath: 'non-existant-file.html'})).rejects.toThrow(FileNotFoundError);
});
Expand Down
3 changes: 3 additions & 0 deletions test/fixtures/folder/styles/issue-566.css
@@ -0,0 +1,3 @@
#element {
color: aqua;
}
15 changes: 15 additions & 0 deletions test/fixtures/issue-566.html
@@ -0,0 +1,15 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Issue 566</title>
<base href="/folder/">
<link rel="stylesheet" href="/styles/issue-566.css" />
</head>
<body>
<h1>Issue 566</h1>
<div id="element"><p>Lorem ipsum</p></div>
</body>
</html>

0 comments on commit d498bc3

Please sign in to comment.