Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix hash in filenames #442

Merged
merged 10 commits into from
Nov 24, 2024
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
141 changes: 120 additions & 21 deletions dist/jsenv_core.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import os, { networkInterfaces } from "node:os";
import tty from "node:tty";
import stringWidth from "string-width";
import { pathToFileURL, fileURLToPath } from "node:url";
import { readdir, chmod, stat, lstat, promises, readFileSync, writeFileSync as writeFileSync$1, mkdirSync, unlink, openSync, closeSync, rmdir, watch, readdirSync, statSync, createReadStream, lstatSync, readFile, existsSync, realpathSync } from "node:fs";
import { readdir, chmod, stat, lstat, chmodSync, statSync, lstatSync, promises, readFileSync, writeFileSync as writeFileSync$1, mkdirSync, unlink, openSync, closeSync, rmdir, watch, readdirSync, createReadStream, readFile, existsSync, realpathSync } from "node:fs";
import { extname } from "node:path";
import crypto, { createHash } from "node:crypto";
import cluster from "node:cluster";
Expand Down Expand Up @@ -988,7 +988,7 @@ platform === 'Android'
const ESC = '\u001B[';

!isBrowser && process$1.env.TERM_PROGRAM === 'Apple_Terminal';
const isWindows$3 = !isBrowser && process$1.platform === 'win32';
const isWindows$4 = !isBrowser && process$1.platform === 'win32';

isBrowser ? () => {
throw new Error('`process.cwd()` only works in Node.js, not the browser.');
Expand All @@ -1014,7 +1014,7 @@ const eraseLines = count => {
const eraseLine = ESC + '2K';
const eraseScreen = ESC + '2J';

const clearTerminal = isWindows$3
const clearTerminal = isWindows$4
? `${eraseScreen}${ESC}0f`
// 1. Erases the screen (Only done in case `2` is not supported)
// 2. Erases the whole screen including scrollback buffer
Expand Down Expand Up @@ -1808,13 +1808,21 @@ const urlIsInsideOf = (url, otherUrl) => {
};

const urlToFileSystemPath = (url) => {
let urlString = String(url);
if (urlString[urlString.length - 1] === "/") {
const urlObject = new URL(url);
let urlString;
if (urlObject.hash) {
const origin =
urlObject.protocol === "file:" ? "file://" : urlObject.origin;
urlString = `${origin}${urlObject.pathname}${urlObject.search}%23${urlObject.hash.slice(1)}`;
} else {
urlString = urlObject.href;
}
const fileSystemPath = fileURLToPath(urlString);
if (fileSystemPath[fileSystemPath.length - 1] === "/") {
// remove trailing / so that nodejs path becomes predictable otherwise it logs
// the trailing slash on linux but does not on windows
urlString = urlString.slice(0, -1);
return fileSystemPath.slice(0, -1);
}
const fileSystemPath = fileURLToPath(urlString);
return fileSystemPath;
};

Expand Down Expand Up @@ -1977,7 +1985,7 @@ const comparePathnames = (leftPathame, rightPathname) => {
return 0;
};

const isWindows$2 = process.platform === "win32";
const isWindows$3 = process.platform === "win32";
const baseUrlFallback = fileSystemPathToUrl$1(process.cwd());

/**
Expand All @@ -2000,7 +2008,7 @@ const ensureWindowsDriveLetter = (url, baseUrl) => {
throw new Error(`absolute url expect but got ${url}`);
}

if (!isWindows$2) {
if (!isWindows$3) {
return url;
}

Expand Down Expand Up @@ -3165,7 +3173,7 @@ const writeEntryPermissions = async (source, permissions) => {
*/


const isWindows$1 = process.platform === "win32";
const isWindows$2 = process.platform === "win32";

const readEntryStat = async (
source,
Expand All @@ -3185,7 +3193,7 @@ const readEntryStat = async (
return readStat(sourcePath, {
followLink,
...handleNotFoundOption,
...(isWindows$1
...(isWindows$2
? {
// Windows can EPERM on stat
handlePermissionDeniedError: async (error) => {
Expand Down Expand Up @@ -3250,13 +3258,104 @@ const readStat = (
});
};

const writeEntryPermissionsSync = (source, permissions) => {
const sourceUrl = assertAndNormalizeFileUrl(source);

let binaryFlags;
{
binaryFlags = permissions;
}

chmodSync(new URL(sourceUrl), binaryFlags);
};

/*
* - stats object documentation on Node.js
* https://nodejs.org/docs/latest-v13.x/api/fs.html#fs_class_fs_stats
*/


process.platform === "win32";
const isWindows$1 = process.platform === "win32";

const readEntryStatSync = (
source,
{ nullIfNotFound = false, followLink = true } = {},
) => {
let sourceUrl = assertAndNormalizeFileUrl(source);
if (sourceUrl.endsWith("/")) sourceUrl = sourceUrl.slice(0, -1);

const sourcePath = urlToFileSystemPath(sourceUrl);

const handleNotFoundOption = nullIfNotFound
? {
handleNotFoundError: () => null,
}
: {};

return statSyncNaive(sourcePath, {
followLink,
...handleNotFoundOption,
...(isWindows$1
? {
// Windows can EPERM on stat
handlePermissionDeniedError: (error) => {
console.error(
`trying to fix windows EPERM after stats on ${sourcePath}`,
);

try {
// unfortunately it means we mutate the permissions
// without being able to restore them to the previous value
// (because reading current permission would also throw)
writeEntryPermissionsSync(sourceUrl, 0o666);
const stats = statSyncNaive(sourcePath, {
followLink,
...handleNotFoundOption,
// could not fix the permission error, give up and throw original error
handlePermissionDeniedError: () => {
console.error(`still got EPERM after stats on ${sourcePath}`);
throw error;
},
});
return stats;
} catch (e) {
console.error(
`error while trying to fix windows EPERM after stats on ${sourcePath}: ${e.stack}`,
);
throw error;
}
},
}
: {}),
});
};

const statSyncNaive = (
sourcePath,
{
followLink,
handleNotFoundError = null,
handlePermissionDeniedError = null,
} = {},
) => {
const nodeMethod = followLink ? statSync : lstatSync;

try {
const stats = nodeMethod(sourcePath);
return stats;
} catch (error) {
if (handleNotFoundError && error.code === "ENOENT") {
return handleNotFoundError(error);
}
if (
handlePermissionDeniedError &&
(error.code === "EPERM" || error.code === "EACCES")
) {
return handlePermissionDeniedError(error);
}
throw error;
}
};

const statsToType = (stats) => {
if (stats.isFile()) return "file";
Expand Down Expand Up @@ -3958,7 +4057,7 @@ const registerDirectoryLifecycle = (
try {
const relativeUrl = urlToRelativeUrl(url, source);
const previousInfo = infoMap.get(relativeUrl);
const stat = statSync(new URL(url));
const stat = readEntryStatSync(new URL(url));
const type = statsToType(stat);
const patternValue = previousInfo
? previousInfo.patternValue
Expand Down Expand Up @@ -4035,7 +4134,7 @@ const registerDirectoryLifecycle = (
const removedEntryRelativeUrl = relativeUrlCandidateArray.find(
(relativeUrlCandidate) => {
try {
statSync(new URL(relativeUrlCandidate, sourceUrl));
readEntryStatSync(new URL(relativeUrlCandidate, sourceUrl));
return false;
} catch (e) {
if (e.code === "ENOENT") {
Expand Down Expand Up @@ -4186,8 +4285,8 @@ const registerDirectoryLifecycle = (
}
};
const handleEntryUpdated = (entryInfo) => {
infoMap.set(entryInfo.relativeUrl, entryInfo);
if (updated && entryInfo.patternValue && shouldCallUpdated(entryInfo)) {
infoMap.set(entryInfo.relativeUrl, entryInfo);
updated({
relativeUrl: entryInfo.relativeUrl,
type: entryInfo.type,
Expand Down Expand Up @@ -8686,7 +8785,7 @@ const bundleCss = async (cssUrlInfos) => {
},
resolve(specifier, from) {
const fileUrlObject = new URL(specifier, pathToFileURL(from));
const filePath = fileURLToPath(fileUrlObject);
const filePath = urlToFileSystemPath(fileUrlObject);
return filePath;
},
},
Expand Down Expand Up @@ -8755,7 +8854,7 @@ const minifyCss = async (cssUrlInfo) => {

const targets = runtimeCompatToTargets$1(cssUrlInfo.context.runtimeCompat);
const { code, map } = transform({
filename: fileURLToPath(cssUrlInfo.originalUrl),
filename: urlToFileSystemPath(cssUrlInfo.originalUrl),
code: Buffer.from(cssUrlInfo.content),
targets,
minify: true,
Expand Down Expand Up @@ -10601,7 +10700,7 @@ const applyCssTranspilation = async ({
const { transform } = await import("lightningcss");
const targets = runtimeCompatToTargets(runtimeCompat);
const { code, map } = transform({
filename: fileURLToPath(inputUrl),
filename: urlToFileSystemPath(inputUrl),
code: Buffer.from(input),
targets,
minify: false,
Expand Down Expand Up @@ -18276,7 +18375,7 @@ const applyFileSystemMagicResolution = (

if (fileStat === undefined) {
try {
fileStat = statSync(new URL(fileUrl));
fileStat = readEntryStatSync(new URL(fileUrl));
} catch (e) {
if (e.code === "ENOENT") {
result.lastENOENTError = e;
Expand Down Expand Up @@ -18318,7 +18417,7 @@ const applyFileSystemMagicResolution = (
const urlCandidate = `${parentUrl}${urlFilename}${extensionToTry}`;
let stat;
try {
stat = statSync(new URL(urlCandidate));
stat = readEntryStatSync(new URL(urlCandidate));
} catch (e) {
if (e.code === "ENOENT") {
stat = null;
Expand Down Expand Up @@ -18679,7 +18778,7 @@ const jsenvPluginFsRedirection = ({
const urlObject = new URL(reference.url);
let stat;
try {
stat = statSync(urlObject);
stat = readEntryStatSync(urlObject);
} catch (e) {
if (e.code === "ENOENT") {
stat = null;
Expand Down
Empty file added experiments/stat_hash/#.txt
Empty file.
5 changes: 5 additions & 0 deletions experiments/stat_hash/stat_hash.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { statSync } from 'node:fs'

const url = new URL('./#.txt', import.meta.url)
const stat = statSync(url)
console.log(stat.isDirectory())
18 changes: 9 additions & 9 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@jsenv/core",
"version": "39.5.24",
"version": "39.5.25",
"description": "Tool to develop, test and build js projects",
"license": "MIT",
"author": {
Expand Down Expand Up @@ -69,22 +69,22 @@
"dependencies": {
"@financial-times/polyfill-useragent-normaliser": "1.10.2",
"@jsenv/abort": "4.3.0",
"@jsenv/ast": "6.3.5",
"@jsenv/filesystem": "4.10.11",
"@jsenv/ast": "6.3.6",
"@jsenv/filesystem": "4.10.12",
"@jsenv/humanize": "1.2.8",
"@jsenv/importmap": "1.2.1",
"@jsenv/integrity": "0.0.2",
"@jsenv/js-module-fallback": "1.3.49",
"@jsenv/js-module-fallback": "1.3.50",
"@jsenv/node-esm-resolution": "1.0.6",
"@jsenv/plugin-bundling": "2.7.16",
"@jsenv/plugin-bundling": "2.7.17",
"@jsenv/plugin-minification": "1.5.10",
"@jsenv/plugin-supervisor": "1.5.28",
"@jsenv/plugin-transpilation": "1.4.84",
"@jsenv/plugin-supervisor": "1.5.29",
"@jsenv/plugin-transpilation": "1.4.85",
"@jsenv/runtime-compat": "1.3.1",
"@jsenv/server": "15.3.3",
"@jsenv/sourcemap": "1.2.25",
"@jsenv/sourcemap": "1.2.26",
"@jsenv/url-meta": "8.5.2",
"@jsenv/urls": "2.5.2",
"@jsenv/urls": "2.5.3",
"@jsenv/utils": "2.1.2",
"string-width": "7.2.0"
},
Expand Down
4 changes: 2 additions & 2 deletions packages/independent/eslint-config-relax/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@jsenv/eslint-config-relax",
"version": "1.2.12",
"version": "1.2.13",
"description": "A pleaseant ESLint configuration for any project",
"license": "MIT",
"author": {
Expand Down Expand Up @@ -33,7 +33,7 @@
"@babel/eslint-parser": "7.25.9",
"globals": "15.12.0",
"@jsenv/eslint-import-resolver": "8.2.0",
"@jsenv/urls": "2.5.2",
"@jsenv/urls": "2.5.3",
"eslint-plugin-import-x": "4.4.0",
"eslint-plugin-html": "8.1.2",
"eslint-plugin-react": "7.37.2",
Expand Down
Loading
Loading