@jsenv/core 39.7.7 → 39.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jsenv/core",
3
- "version": "39.7.7",
3
+ "version": "39.8.1",
4
4
  "description": "Tool to develop, test and build js projects",
5
5
  "license": "MIT",
6
6
  "author": {
@@ -70,19 +70,19 @@
70
70
  "@financial-times/polyfill-useragent-normaliser": "1.10.2",
71
71
  "@jsenv/abort": "4.3.0",
72
72
  "@jsenv/ast": "6.4.1",
73
- "@jsenv/filesystem": "4.11.0",
73
+ "@jsenv/filesystem": "4.12.0",
74
74
  "@jsenv/humanize": "1.2.8",
75
75
  "@jsenv/importmap": "1.2.1",
76
76
  "@jsenv/integrity": "0.0.2",
77
- "@jsenv/js-module-fallback": "1.3.53",
77
+ "@jsenv/js-module-fallback": "1.3.54",
78
78
  "@jsenv/node-esm-resolution": "1.0.6",
79
- "@jsenv/plugin-bundling": "2.7.20",
79
+ "@jsenv/plugin-bundling": "2.7.21",
80
80
  "@jsenv/plugin-minification": "1.5.12",
81
- "@jsenv/plugin-supervisor": "1.6.0",
82
- "@jsenv/plugin-transpilation": "1.4.88",
81
+ "@jsenv/plugin-supervisor": "1.6.1",
82
+ "@jsenv/plugin-transpilation": "1.4.89",
83
83
  "@jsenv/runtime-compat": "1.3.1",
84
84
  "@jsenv/server": "15.3.3",
85
- "@jsenv/sourcemap": "1.2.28",
85
+ "@jsenv/sourcemap": "1.2.29",
86
86
  "@jsenv/url-meta": "8.5.2",
87
87
  "@jsenv/urls": "2.5.4",
88
88
  "@jsenv/utils": "2.1.2",
@@ -108,7 +108,7 @@
108
108
  "@playwright/browser-firefox": "1.49.1",
109
109
  "@playwright/browser-webkit": "1.49.1",
110
110
  "babel-plugin-transform-async-to-promises": "0.8.18",
111
- "eslint": "9.16.0",
111
+ "eslint": "9.17.0",
112
112
  "open": "10.1.0",
113
113
  "playwright": "1.49.1",
114
114
  "prettier": "3.4.2",
@@ -1,37 +1,9 @@
1
+ import { findAncestorDirectoryUrl } from "@jsenv/filesystem";
1
2
  import { existsSync } from "node:fs";
2
3
 
3
4
  export const lookupPackageDirectory = (currentUrl) => {
4
- if (currentUrl === "file:///") {
5
- return null;
6
- }
7
- const packageJsonFileUrl = `${currentUrl}package.json`;
8
- if (existsSync(new URL(packageJsonFileUrl))) {
9
- return currentUrl;
10
- }
11
- return lookupPackageDirectory(getParentUrl(currentUrl));
12
- };
13
-
14
- const getParentUrl = (url) => {
15
- if (url.startsWith("file://")) {
16
- // With node.js new URL('../', 'file:///C:/').href
17
- // returns "file:///C:/" instead of "file:///"
18
- const resource = url.slice("file://".length);
19
- const slashLastIndex = resource.lastIndexOf("/");
20
- if (slashLastIndex === -1) {
21
- return url;
22
- }
23
- const lastCharIndex = resource.length - 1;
24
- if (slashLastIndex === lastCharIndex) {
25
- const slashBeforeLastIndex = resource.lastIndexOf(
26
- "/",
27
- slashLastIndex - 1,
28
- );
29
- if (slashBeforeLastIndex === -1) {
30
- return url;
31
- }
32
- return `file://${resource.slice(0, slashBeforeLastIndex + 1)}`;
33
- }
34
- return `file://${resource.slice(0, slashLastIndex + 1)}`;
35
- }
36
- return new URL(url.endsWith("/") ? "../" : "./", url).href;
5
+ return findAncestorDirectoryUrl(currentUrl, (ancestorDirectoryUrl) => {
6
+ const potentialPackageJsonFileUrl = `${ancestorDirectoryUrl}package.json`;
7
+ return existsSync(new URL(potentialPackageJsonFileUrl));
8
+ });
37
9
  };
@@ -1,4 +1,4 @@
1
- import { removeDirectorySync, writeFileSync } from "@jsenv/filesystem";
1
+ import { writeFileSync } from "@jsenv/filesystem";
2
2
  import {
3
3
  composeTwoSourcemaps,
4
4
  generateSourcemapDataUrl,
@@ -275,18 +275,7 @@ export const createUrlInfoTransformer = ({
275
275
  contentIsInlined = false;
276
276
  }
277
277
  if (!contentIsInlined) {
278
- try {
279
- writeFileSync(new URL(generatedUrl), urlInfo.content);
280
- } catch (e) {
281
- if (e.code === "EISDIR") {
282
- // happens when directory existed but got delete
283
- // we can safely remove that directory and write the new file
284
- removeDirectorySync(new URL(generatedUrl));
285
- writeFileSync(new URL(generatedUrl), urlInfo.content);
286
- } else {
287
- throw e;
288
- }
289
- }
278
+ writeFileSync(new URL(generatedUrl), urlInfo.content, { force: true });
290
279
  }
291
280
  const { sourcemapGeneratedUrl, sourcemapReference } = urlInfo;
292
281
  if (sourcemapGeneratedUrl && sourcemapReference) {
@@ -11,6 +11,7 @@ import {
11
11
  } from "@jsenv/urls";
12
12
  import { CONTENT_TYPE } from "@jsenv/utils/src/content_type/content_type.js";
13
13
  import { existsSync, lstatSync, readdirSync, readFileSync } from "node:fs";
14
+ import { lookupPackageDirectory } from "../../helpers/lookup_package_directory.js";
14
15
  import { jsenvCoreDirectoryUrl } from "../../jsenv_core_directory_url.js";
15
16
  import { jsenvPluginFsRedirection } from "./jsenv_plugin_fs_redirection.js";
16
17
 
@@ -83,6 +84,7 @@ export const jsenvPluginProtocolFile = ({
83
84
  if (!urlInfo.url.startsWith("file:")) {
84
85
  return null;
85
86
  }
87
+ const { rootDirectoryUrl } = urlInfo.context;
86
88
  const generateContent = () => {
87
89
  const urlObject = new URL(urlInfo.url);
88
90
  const { firstReference } = urlInfo;
@@ -101,11 +103,12 @@ export const jsenvPluginProtocolFile = ({
101
103
  : false;
102
104
  if (acceptsHtml) {
103
105
  firstReference.expectedType = "html";
104
- const html = generateHtmlForDirectory(
105
- urlObject.href,
106
- directoryContentArray,
107
- urlInfo.context.rootDirectoryUrl,
106
+ const directoryUrl = urlObject.href;
107
+ const directoryContentItems = generateDirectoryContentItems(
108
+ directoryUrl,
109
+ rootDirectoryUrl,
108
110
  );
111
+ const html = generateHtmlForDirectory(directoryContentItems);
109
112
  return {
110
113
  type: "html",
111
114
  contentType: "text/html",
@@ -138,32 +141,13 @@ export const jsenvPluginProtocolFile = ({
138
141
  if (e.code !== "ENOENT") {
139
142
  throw e;
140
143
  }
141
- const rootDirectoryUrl = urlInfo.context.rootDirectoryUrl;
142
- let firstExistingAncestorDirectoryUrl = new URL("./", urlInfo.url);
143
- while (!existsSync(firstExistingAncestorDirectoryUrl)) {
144
- firstExistingAncestorDirectoryUrl = new URL(
145
- "../",
146
- firstExistingAncestorDirectoryUrl,
147
- );
148
- if (
149
- !urlIsInsideOf(
150
- firstExistingAncestorDirectoryUrl,
151
- rootDirectoryUrl,
152
- )
153
- ) {
154
- firstExistingAncestorDirectoryUrl = rootDirectoryUrl;
155
- break;
156
- }
157
- }
158
-
159
- const firstExistingAncestorDirectoryContent = readdirSync(
160
- new URL(firstExistingAncestorDirectoryUrl),
144
+ const directoryContentItems = generateDirectoryContentItems(
145
+ urlInfo.url,
146
+ rootDirectoryUrl,
161
147
  );
162
148
  const html = generateHtmlForENOENT(
163
149
  urlInfo.url,
164
- firstExistingAncestorDirectoryContent,
165
- firstExistingAncestorDirectoryUrl,
166
- urlInfo.context.rootDirectoryUrl,
150
+ directoryContentItems,
167
151
  directoryListingUrlMocks,
168
152
  );
169
153
  return {
@@ -182,11 +166,9 @@ export const jsenvPluginProtocolFile = ({
182
166
  ];
183
167
  };
184
168
 
185
- const generateHtmlForDirectory = (
186
- directoryUrl,
187
- directoryContentArray,
188
- rootDirectoryUrl,
189
- ) => {
169
+ const generateHtmlForDirectory = (directoryContentItems) => {
170
+ let directoryUrl = directoryContentItems.firstExistingDirectoryUrl;
171
+ const rootDirectoryUrl = directoryContentItems.rootDirectoryUrl;
190
172
  directoryUrl = assertAndNormalizeDirectoryUrl(directoryUrl);
191
173
 
192
174
  const htmlForDirectory = String(readFileSync(htmlFileUrlForDirectory));
@@ -195,23 +177,19 @@ const generateHtmlForDirectory = (
195
177
  directoryUrl,
196
178
  directoryNav: () =>
197
179
  generateDirectoryNav(directoryRelativeUrl, rootDirectoryUrl),
198
- directoryContent: () =>
199
- generateDirectoryContent(
200
- directoryContentArray,
201
- directoryUrl,
202
- rootDirectoryUrl,
203
- ),
180
+ directoryContent: () => generateDirectoryContent(directoryContentItems),
204
181
  };
205
182
  const html = replacePlaceholders(htmlForDirectory, replacers);
206
183
  return html;
207
184
  };
208
185
  const generateHtmlForENOENT = (
209
186
  url,
210
- ancestorDirectoryContentArray,
211
- ancestorDirectoryUrl,
212
- rootDirectoryUrl,
187
+ directoryContentItems,
213
188
  directoryListingUrlMocks,
214
189
  ) => {
190
+ const ancestorDirectoryUrl = directoryContentItems.firstExistingDirectoryUrl;
191
+ const rootDirectoryUrl = directoryContentItems.rootDirectoryUrl;
192
+
215
193
  const htmlFor404AndAncestorDir = String(
216
194
  readFileSync(html404AndAncestorDirFileUrl),
217
195
  );
@@ -230,11 +208,7 @@ const generateHtmlForENOENT = (
230
208
  ancestorDirectoryNav: () =>
231
209
  generateDirectoryNav(ancestorDirectoryRelativeUrl, rootDirectoryUrl),
232
210
  ancestorDirectoryContent: () =>
233
- generateDirectoryContent(
234
- ancestorDirectoryContentArray,
235
- ancestorDirectoryUrl,
236
- rootDirectoryUrl,
237
- ),
211
+ generateDirectoryContent(directoryContentItems),
238
212
  };
239
213
  const html = replacePlaceholders(htmlFor404AndAncestorDir, replacers);
240
214
  return html;
@@ -276,31 +250,95 @@ const generateDirectoryNav = (relativeUrl, rootDirectoryUrl) => {
276
250
  }
277
251
  return dirPartsHtml;
278
252
  };
279
- const generateDirectoryContent = (
280
- directoryContentArray,
281
- directoryUrl,
282
- rootDirectoryUrl,
283
- ) => {
284
- if (directoryContentArray.length === 0) {
285
- return `<p>Directory is empty</p>`;
253
+ const generateDirectoryContentItems = (directoryUrl, rootDirectoryUrl) => {
254
+ let firstExistingDirectoryUrl = new URL("./", directoryUrl);
255
+ while (!existsSync(firstExistingDirectoryUrl)) {
256
+ firstExistingDirectoryUrl = new URL("../", firstExistingDirectoryUrl);
257
+ if (!urlIsInsideOf(firstExistingDirectoryUrl, rootDirectoryUrl)) {
258
+ firstExistingDirectoryUrl = new URL(rootDirectoryUrl);
259
+ break;
260
+ }
286
261
  }
287
- const sortedNames = [];
262
+ const directoryContentArray = readdirSync(firstExistingDirectoryUrl);
263
+ const fileUrls = [];
288
264
  for (const filename of directoryContentArray) {
289
- const fileUrlObject = new URL(filename, directoryUrl);
290
- if (lstatSync(fileUrlObject).isDirectory()) {
291
- sortedNames.push(`${filename}/`);
265
+ const fileUrlObject = new URL(filename, firstExistingDirectoryUrl);
266
+ fileUrls.push(fileUrlObject);
267
+ }
268
+ package_workspaces: {
269
+ if (String(firstExistingDirectoryUrl) !== String(rootDirectoryUrl)) {
270
+ break package_workspaces;
271
+ }
272
+ const packageDirectoryUrl = lookupPackageDirectory(rootDirectoryUrl);
273
+ if (!packageDirectoryUrl) {
274
+ break package_workspaces;
275
+ }
276
+ if (String(packageDirectoryUrl) === String(rootDirectoryUrl)) {
277
+ break package_workspaces;
278
+ }
279
+ let packageContent;
280
+ try {
281
+ packageContent = JSON.parse(
282
+ readFileSync(new URL("package.json", packageDirectoryUrl), "utf8"),
283
+ );
284
+ } catch {
285
+ break package_workspaces;
286
+ }
287
+ const { workspaces } = packageContent;
288
+ if (Array.isArray(workspaces)) {
289
+ for (const workspace of workspaces) {
290
+ const workspaceUrlObject = new URL(workspace, packageDirectoryUrl);
291
+ const workspaceUrl = workspaceUrlObject.href;
292
+ if (workspaceUrl.endsWith("*")) {
293
+ const directoryUrl = ensurePathnameTrailingSlash(
294
+ workspaceUrl.slice(0, -1),
295
+ );
296
+ fileUrls.push(new URL(directoryUrl));
297
+ } else {
298
+ fileUrls.push(ensurePathnameTrailingSlash(workspaceUrlObject));
299
+ }
300
+ }
301
+ }
302
+ }
303
+
304
+ const sortedUrls = [];
305
+ for (let fileUrl of fileUrls) {
306
+ if (lstatSync(fileUrl).isDirectory()) {
307
+ sortedUrls.push(ensurePathnameTrailingSlash(fileUrl));
292
308
  } else {
293
- sortedNames.push(filename);
309
+ sortedUrls.push(fileUrl);
294
310
  }
295
311
  }
296
- sortedNames.sort(comparePathnames);
297
- let html = `<ul class="directory_content">`;
298
- for (const filename of sortedNames) {
299
- const fileUrlObject = new URL(filename, directoryUrl);
300
- const fileUrl = String(fileUrlObject);
301
- const fileUrlRelativeToParent = urlToRelativeUrl(fileUrl, directoryUrl);
302
- const fileUrlRelativeToRoot = urlToRelativeUrl(fileUrl, rootDirectoryUrl);
312
+ sortedUrls.sort((a, b) => {
313
+ return comparePathnames(a.pathname, b.pathname);
314
+ });
315
+
316
+ const items = [];
317
+ for (const sortedUrl of sortedUrls) {
318
+ const fileUrlRelativeToParent = urlToRelativeUrl(
319
+ sortedUrl,
320
+ firstExistingDirectoryUrl,
321
+ );
322
+ const fileUrlRelativeToRoot = urlToRelativeUrl(sortedUrl, rootDirectoryUrl);
303
323
  const type = fileUrlRelativeToParent.endsWith("/") ? "dir" : "file";
324
+ items.push({
325
+ type,
326
+ fileUrlRelativeToParent,
327
+ fileUrlRelativeToRoot,
328
+ });
329
+ }
330
+ items.rootDirectoryUrl = rootDirectoryUrl;
331
+ items.firstExistingDirectoryUrl = firstExistingDirectoryUrl;
332
+ return items;
333
+ };
334
+ const generateDirectoryContent = (directoryContentItems) => {
335
+ if (directoryContentItems.length === 0) {
336
+ return `<p>Directory is empty</p>`;
337
+ }
338
+ let html = `<ul class="directory_content">`;
339
+ for (const directoryContentItem of directoryContentItems) {
340
+ const { type, fileUrlRelativeToParent, fileUrlRelativeToRoot } =
341
+ directoryContentItem;
304
342
  html += `
305
343
  <li class="directory_child" data-type="${type}">
306
344
  <a href="/${fileUrlRelativeToRoot}">${fileUrlRelativeToParent}</a>