@jsenv/core 27.3.1 → 27.3.4

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": "27.3.1",
3
+ "version": "27.3.4",
4
4
  "description": "Tool to develop, test and build js projects",
5
5
  "license": "MIT",
6
6
  "author": {
@@ -738,7 +738,7 @@ build ${entryPointKeys.length} entry points`)
738
738
 
739
739
  logger.debug(
740
740
  `graph urls pre-versioning:
741
- ${Object.keys(finalGraph.urlInfos).join("\n")}`,
741
+ ${Array.from(finalGraph.urlInfoMap.keys()).join("\n")}`,
742
742
  )
743
743
  if (versioning) {
744
744
  await applyUrlVersioning({
@@ -973,7 +973,7 @@ const applyUrlVersioning = async ({
973
973
  disabled: infoLogsAreDisabled,
974
974
  })
975
975
  try {
976
- const urlsSorted = sortByDependencies(finalGraph.urlInfos)
976
+ const urlsSorted = sortByDependencies(finalGraph.toObject())
977
977
  urlsSorted.forEach((url) => {
978
978
  if (url.startsWith("data:")) {
979
979
  return
@@ -1,20 +1,19 @@
1
1
  export const GRAPH = {
2
2
  map: (graph, callback) => {
3
- return Object.keys(graph.urlInfos).map((url) => {
4
- return callback(graph.urlInfos[url])
3
+ const array = []
4
+ graph.urlInfoMap.forEach((urlInfo) => {
5
+ array.push(callback(urlInfo))
5
6
  })
7
+ return array
6
8
  },
7
9
 
8
10
  forEach: (graph, callback) => {
9
- Object.keys(graph.urlInfos).forEach((url) => {
10
- callback(graph.urlInfos[url], url)
11
- })
11
+ graph.urlInfoMap.forEach(callback)
12
12
  },
13
13
 
14
14
  filter: (graph, callback) => {
15
15
  const urlInfos = []
16
- Object.keys(graph.urlInfos).forEach((url) => {
17
- const urlInfo = graph.urlInfos[url]
16
+ graph.urlInfoMap.forEach((urlInfo) => {
18
17
  if (callback(urlInfo)) {
19
18
  urlInfos.push(urlInfo)
20
19
  }
@@ -23,9 +22,13 @@ export const GRAPH = {
23
22
  },
24
23
 
25
24
  find: (graph, callback) => {
26
- const urlFound = Object.keys(graph.urlInfos).find((url) => {
27
- return callback(graph.urlInfos[url])
28
- })
29
- return graph.urlInfos[urlFound]
25
+ let found = null
26
+ for (const urlInfo of graph.urlInfoMap.values()) {
27
+ if (callback(urlInfo)) {
28
+ found = urlInfo
29
+ break
30
+ }
31
+ }
32
+ return found
30
33
  },
31
34
  }
@@ -1,6 +1,6 @@
1
1
  import cuid from "cuid"
2
2
  import { Abort, raceCallbacks } from "@jsenv/abort"
3
- import { writeFileSync } from "@jsenv/filesystem"
3
+ import { ensureParentDirectories } from "@jsenv/filesystem"
4
4
 
5
5
  export const run = async ({
6
6
  signal = new AbortController().signal,
@@ -16,7 +16,7 @@ export const run = async ({
16
16
  runtime,
17
17
  runtimeParams,
18
18
  }) => {
19
- let result = {}
19
+ const result = {}
20
20
  const callbacks = []
21
21
 
22
22
  const onConsoleRef = { current: () => {} }
@@ -39,17 +39,15 @@ export const run = async ({
39
39
  Abort.isAbortError(result.error) &&
40
40
  timeoutAbortSource.signal.aborted
41
41
  ) {
42
- result = {
43
- status: "timedout",
44
- }
42
+ result.status = "timedout"
43
+ delete result.error
45
44
  }
46
45
  })
47
46
  }
48
47
  callbacks.push(() => {
49
48
  if (result.status === "errored" && Abort.isAbortError(result.error)) {
50
- result = {
51
- status: "aborted",
52
- }
49
+ result.status = "aborted"
50
+ delete result.error
53
51
  }
54
52
  })
55
53
  const consoleCalls = []
@@ -71,6 +69,24 @@ export const run = async ({
71
69
  })
72
70
  }
73
71
 
72
+ // we do not keep coverage in memory, it can grow very big
73
+ // instead we store it on the filesystem
74
+ // and they can be read later at "coverageFileUrl"
75
+ let coverageFileUrl
76
+ if (coverageEnabled) {
77
+ coverageFileUrl = new URL(
78
+ `./${runtime.name}/${cuid()}.json`,
79
+ coverageTempDirectoryUrl,
80
+ ).href
81
+ await ensureParentDirectories(coverageFileUrl)
82
+ if (coverageEnabled) {
83
+ result.coverageFileUrl = coverageFileUrl
84
+ // written within the child_process/worker_thread or during runtime.run()
85
+ // for browsers
86
+ // (because it takes time to serialize and transfer the coverage object)
87
+ }
88
+ }
89
+
74
90
  const startMs = Date.now()
75
91
  callbacks.push(() => {
76
92
  result.duration = Date.now() - startMs
@@ -94,7 +110,9 @@ export const run = async ({
94
110
  signal: runOperation.signal,
95
111
  logger,
96
112
  ...runtimeParams,
113
+ collectConsole,
97
114
  collectPerformance,
115
+ coverageFileUrl,
98
116
  keepRunning,
99
117
  stopSignal,
100
118
  onConsole: (log) => onConsoleRef.current(log),
@@ -116,7 +134,7 @@ export const run = async ({
116
134
  runOperation.throwIfAborted()
117
135
  }
118
136
 
119
- const { status, namespace, error, performance, coverage } = winner.data
137
+ const { status, namespace, error, performance } = winner.data
120
138
  result.status = status
121
139
  if (status === "errored") {
122
140
  result.error = error
@@ -126,30 +144,13 @@ export const run = async ({
126
144
  if (collectPerformance) {
127
145
  result.performance = performance
128
146
  }
129
- if (coverageEnabled) {
130
- if (coverage) {
131
- // we do not keep coverage in memory, it can grow very big
132
- // instead we store it on the filesystem
133
- // and they can be read later at "coverageFileUrl"
134
- const coverageFileUrl = new URL(
135
- `./${runtime.name}/${cuid()}.json`,
136
- coverageTempDirectoryUrl,
137
- )
138
- writeFileSync(coverageFileUrl, JSON.stringify(coverage, null, " "))
139
- result.coverageFileUrl = coverageFileUrl.href
140
- } else {
141
- // will eventually log a warning in report_to_coverage.js
142
- }
143
- }
144
147
  callbacks.forEach((callback) => {
145
148
  callback()
146
149
  })
147
150
  return result
148
151
  } catch (e) {
149
- result = {
150
- status: "errored",
151
- error: e,
152
- }
152
+ result.status = "errored"
153
+ result.error = e
153
154
  callbacks.forEach((callback) => {
154
155
  callback()
155
156
  })
@@ -1,4 +1,5 @@
1
1
  import { Script } from "node:vm"
2
+ import { writeFileSync } from "node:fs"
2
3
 
3
4
  import { createDetailedMessage } from "@jsenv/log"
4
5
  import {
@@ -41,6 +42,7 @@ export const createRuntimeFromPlaywright = ({
41
42
  coverageEnabled = false,
42
43
  coverageConfig,
43
44
  coverageMethodForBrowsers,
45
+ coverageFileUrl,
44
46
 
45
47
  stopAfterAllSignal,
46
48
  stopSignal,
@@ -108,7 +110,8 @@ export const createRuntimeFromPlaywright = ({
108
110
  }
109
111
  }
110
112
 
111
- let resultTransformer = (result) => result
113
+ const result = {}
114
+ const callbacks = []
112
115
  if (coverageEnabled) {
113
116
  if (
114
117
  coveragePlaywrightAPIAvailable &&
@@ -117,93 +120,86 @@ export const createRuntimeFromPlaywright = ({
117
120
  await page.coverage.startJSCoverage({
118
121
  // reportAnonymousScripts: true,
119
122
  })
120
- resultTransformer = composeTransformer(
121
- resultTransformer,
122
- async (result) => {
123
- const v8CoveragesWithWebUrls = await page.coverage.stopJSCoverage()
124
- // we convert urls starting with http:// to file:// because we later
125
- // convert the url to filesystem path in istanbulCoverageFromV8Coverage function
126
- const v8CoveragesWithFsUrls = v8CoveragesWithWebUrls.map(
127
- (v8CoveragesWithWebUrl) => {
128
- const fsUrl = moveUrl({
129
- url: v8CoveragesWithWebUrl.url,
130
- from: `${server.origin}/`,
131
- to: rootDirectoryUrl,
132
- preferAbsolute: true,
133
- })
134
- return {
135
- ...v8CoveragesWithWebUrl,
136
- url: fsUrl,
137
- }
138
- },
139
- )
140
- const coverage = await filterV8Coverage(
141
- { result: v8CoveragesWithFsUrls },
142
- {
143
- rootDirectoryUrl,
144
- coverageConfig,
145
- },
146
- )
147
- return {
148
- ...result,
149
- coverage,
150
- }
151
- },
152
- )
123
+ callbacks.push(async () => {
124
+ const v8CoveragesWithWebUrls = await page.coverage.stopJSCoverage()
125
+ // we convert urls starting with http:// to file:// because we later
126
+ // convert the url to filesystem path in istanbulCoverageFromV8Coverage function
127
+ const v8CoveragesWithFsUrls = v8CoveragesWithWebUrls.map(
128
+ (v8CoveragesWithWebUrl) => {
129
+ const fsUrl = moveUrl({
130
+ url: v8CoveragesWithWebUrl.url,
131
+ from: `${server.origin}/`,
132
+ to: rootDirectoryUrl,
133
+ preferAbsolute: true,
134
+ })
135
+ return {
136
+ ...v8CoveragesWithWebUrl,
137
+ url: fsUrl,
138
+ }
139
+ },
140
+ )
141
+ const coverage = await filterV8Coverage(
142
+ { result: v8CoveragesWithFsUrls },
143
+ {
144
+ rootDirectoryUrl,
145
+ coverageConfig,
146
+ },
147
+ )
148
+ writeFileSync(
149
+ new URL(coverageFileUrl),
150
+ JSON.stringify(coverage, null, " "),
151
+ )
152
+ })
153
153
  } else {
154
- resultTransformer = composeTransformer(
155
- resultTransformer,
156
- async (result) => {
157
- const scriptExecutionResults = result.namespace
158
- if (scriptExecutionResults) {
159
- result.coverage = generateCoverageForPage(scriptExecutionResults)
160
- }
161
- return result
162
- },
163
- )
154
+ callbacks.push(() => {
155
+ const scriptExecutionResults = result.namespace
156
+ if (scriptExecutionResults) {
157
+ const coverage =
158
+ generateCoverageForPage(scriptExecutionResults) || {}
159
+ writeFileSync(
160
+ new URL(coverageFileUrl),
161
+ JSON.stringify(coverage, null, " "),
162
+ )
163
+ }
164
+ })
164
165
  }
165
166
  } else {
166
- resultTransformer = composeTransformer(resultTransformer, (result) => {
167
+ callbacks.push(() => {
167
168
  const scriptExecutionResults = result.namespace
168
169
  if (scriptExecutionResults) {
169
170
  Object.keys(scriptExecutionResults).forEach((fileRelativeUrl) => {
170
171
  delete scriptExecutionResults[fileRelativeUrl].coverage
171
172
  })
172
173
  }
173
- return result
174
174
  })
175
175
  }
176
176
 
177
177
  if (collectPerformance) {
178
- resultTransformer = composeTransformer(
179
- resultTransformer,
180
- async (result) => {
181
- const performance = await page.evaluate(
182
- /* eslint-disable no-undef */
183
- /* istanbul ignore next */
184
- () => {
185
- const { performance } = window
186
- if (!performance) {
187
- return null
188
- }
189
- const measures = {}
190
- const measurePerfEntries = performance.getEntriesByType("measure")
191
- measurePerfEntries.forEach((measurePerfEntry) => {
192
- measures[measurePerfEntry.name] = measurePerfEntry.duration
193
- })
194
- return {
195
- timeOrigin: performance.timeOrigin,
196
- timing: performance.timing.toJSON(),
197
- navigation: performance.navigation.toJSON(),
198
- measures,
199
- }
200
- },
201
- /* eslint-enable no-undef */
202
- )
203
- result.performance = performance
204
- return result
205
- },
206
- )
178
+ callbacks.push(async () => {
179
+ const performance = await page.evaluate(
180
+ /* eslint-disable no-undef */
181
+ /* istanbul ignore next */
182
+ () => {
183
+ const { performance } = window
184
+ if (!performance) {
185
+ return null
186
+ }
187
+ const measures = {}
188
+ const measurePerfEntries = performance.getEntriesByType("measure")
189
+ measurePerfEntries.forEach((measurePerfEntry) => {
190
+ measures[measurePerfEntry.name] = measurePerfEntry.duration
191
+ })
192
+ return {
193
+ timeOrigin: performance.timeOrigin,
194
+ timing: performance.timing.toJSON(),
195
+ navigation: performance.navigation.toJSON(),
196
+ measures,
197
+ }
198
+ },
199
+ /* eslint-enable no-undef */
200
+ )
201
+ result.performance = performance
202
+ })
207
203
  }
208
204
 
209
205
  const fileClientUrl = new URL(fileRelativeUrl, `${server.origin}/`).href
@@ -290,7 +286,7 @@ export const createRuntimeFromPlaywright = ({
290
286
  response: async (cb) => {
291
287
  try {
292
288
  await page.goto(fileClientUrl, { timeout: 0 })
293
- const result = await page.evaluate(
289
+ const returnValue = await page.evaluate(
294
290
  /* eslint-disable no-undef */
295
291
  /* istanbul ignore next */
296
292
  () => {
@@ -301,9 +297,9 @@ export const createRuntimeFromPlaywright = ({
301
297
  },
302
298
  /* eslint-enable no-undef */
303
299
  )
304
- const { status, scriptExecutionResults } = result
300
+ const { status, scriptExecutionResults } = returnValue
305
301
  if (status === "errored") {
306
- const { exceptionSource } = result
302
+ const { exceptionSource } = returnValue
307
303
  const error = evalException(exceptionSource, {
308
304
  rootDirectoryUrl,
309
305
  server,
@@ -353,16 +349,25 @@ export const createRuntimeFromPlaywright = ({
353
349
  }
354
350
  return winner.data
355
351
  }
356
- let result
357
352
 
358
353
  try {
359
- result = await getResult()
360
- result = await resultTransformer(result)
361
- } catch (e) {
362
- result = {
363
- status: "errored",
364
- error: e,
354
+ const { status, error, namespace, performance } = await getResult()
355
+ result.status = status
356
+ if (status === "errored") {
357
+ result.error = error
358
+ } else {
359
+ result.namespace = namespace
365
360
  }
361
+ if (collectPerformance) {
362
+ result.performance = performance
363
+ }
364
+ await callbacks.reduce(async (previous, callback) => {
365
+ await previous
366
+ await callback()
367
+ }, Promise.resolve())
368
+ } catch (e) {
369
+ result.status = "errored"
370
+ result.error = e
366
371
  }
367
372
  if (keepRunning) {
368
373
  stopSignal.notify = cleanup
@@ -478,13 +483,6 @@ const isTargetClosedError = (error) => {
478
483
  return false
479
484
  }
480
485
 
481
- const composeTransformer = (previousTransformer, transformer) => {
482
- return async (value) => {
483
- const transformedValue = await previousTransformer(value)
484
- return transformer(transformedValue)
485
- }
486
- }
487
-
488
486
  const extractTextFromConsoleMessage = (consoleMessage) => {
489
487
  return consoleMessage.text()
490
488
  // ensure we use a string so that istanbul won't try
@@ -1,3 +1,5 @@
1
+ import { writeFileSync } from "node:fs"
2
+
1
3
  import { startJsCoverage } from "./profiler_v8_coverage.js"
2
4
  import { startObservingPerformances } from "./node_execution_performance.js"
3
5
 
@@ -8,8 +10,9 @@ export const executeUsingDynamicImport = async ({
8
10
  coverageEnabled,
9
11
  coverageConfig,
10
12
  coverageMethodForNodeJs,
13
+ coverageFileUrl,
11
14
  }) => {
12
- let result = {}
15
+ const result = {}
13
16
  const afterImportCallbacks = []
14
17
  if (coverageEnabled && coverageMethodForNodeJs === "Profiler") {
15
18
  const { filterV8Coverage } = await import(
@@ -22,7 +25,10 @@ export const executeUsingDynamicImport = async ({
22
25
  rootDirectoryUrl,
23
26
  coverageConfig,
24
27
  })
25
- result.coverage = coverageLight
28
+ writeFileSync(
29
+ new URL(coverageFileUrl),
30
+ JSON.stringify(coverageLight, null, " "),
31
+ )
26
32
  })
27
33
  }
28
34
  if (collectPerformance) {
@@ -39,6 +39,7 @@ nodeChildProcess.run = async ({
39
39
  coverageEnabled = false,
40
40
  coverageConfig,
41
41
  coverageMethodForNodeJs,
42
+ coverageFileUrl,
42
43
  collectPerformance,
43
44
 
44
45
  env,
@@ -199,6 +200,7 @@ nodeChildProcess.run = async ({
199
200
  coverageEnabled,
200
201
  coverageConfig,
201
202
  coverageMethodForNodeJs,
203
+ coverageFileUrl,
202
204
  exitAfterAction: true,
203
205
  },
204
206
  },
@@ -35,10 +35,12 @@ nodeWorkerThread.run = async ({
35
35
  stopSignal,
36
36
  onConsole,
37
37
 
38
+ collectConsole = false,
39
+ collectPerformance,
40
+ coverageEnabled = false,
38
41
  coverageConfig,
39
42
  coverageMethodForNodeJs,
40
- coverageEnabled = false,
41
- collectPerformance,
43
+ coverageFileUrl,
42
44
 
43
45
  env,
44
46
  debugPort,
@@ -102,6 +104,14 @@ nodeWorkerThread.run = async ({
102
104
  })
103
105
 
104
106
  const stop = memoize(async () => {
107
+ // read all stdout before terminating
108
+ // (no need for stderr because it's sync)
109
+ if (collectConsole) {
110
+ while (workerThread.stdout.read() !== null) {}
111
+ await new Promise((resolve) => {
112
+ setTimeout(resolve, 50)
113
+ })
114
+ }
105
115
  await workerThread.terminate()
106
116
  })
107
117
 
@@ -142,6 +152,7 @@ nodeWorkerThread.run = async ({
142
152
  coverageEnabled,
143
153
  coverageConfig,
144
154
  coverageMethodForNodeJs,
155
+ coverageFileUrl,
145
156
  exitAfterAction: true,
146
157
  },
147
158
  },
@@ -11,7 +11,6 @@ ${createRepartitionMessage(graphReport)}
11
11
  }
12
12
 
13
13
  const createUrlGraphReport = (urlGraph) => {
14
- const { urlInfos } = urlGraph
15
14
  const countGroups = {
16
15
  sourcemaps: 0,
17
16
  html: 0,
@@ -30,11 +29,10 @@ const createUrlGraphReport = (urlGraph) => {
30
29
  other: 0,
31
30
  total: 0,
32
31
  }
33
- Object.keys(urlInfos).forEach((url) => {
34
- if (url.startsWith("data:")) {
32
+ urlGraph.urlInfoMap.forEach((urlInfo) => {
33
+ if (urlInfo.url.startsWith("data:")) {
35
34
  return
36
35
  }
37
- const urlInfo = urlInfos[url]
38
36
  // ignore:
39
37
  // - inline files: they are already taken into account in the file where they appear
40
38
  // - ignored files: we don't know their content
@@ -5,12 +5,12 @@ export const createUrlGraph = ({
5
5
  clientFileChangeCallbackList,
6
6
  clientFilesPruneCallbackList,
7
7
  } = {}) => {
8
- const urlInfos = {}
9
- const getUrlInfo = (url) => urlInfos[url]
8
+ const urlInfoMap = new Map()
9
+ const getUrlInfo = (url) => urlInfoMap.get(url)
10
10
  const deleteUrlInfo = (url) => {
11
- const urlInfo = urlInfos[url]
11
+ const urlInfo = urlInfoMap.get(url)
12
12
  if (urlInfo) {
13
- delete urlInfos[url]
13
+ urlInfoMap.delete(url)
14
14
  if (urlInfo.sourcemapReference) {
15
15
  deleteUrlInfo(urlInfo.sourcemapReference.url)
16
16
  }
@@ -18,14 +18,14 @@ export const createUrlGraph = ({
18
18
  }
19
19
 
20
20
  const reuseOrCreateUrlInfo = (url) => {
21
- const existingUrlInfo = urlInfos[url]
21
+ const existingUrlInfo = getUrlInfo(url)
22
22
  if (existingUrlInfo) return existingUrlInfo
23
23
  const urlInfo = createUrlInfo(url)
24
- urlInfos[url] = urlInfo
24
+ urlInfoMap.set(url, urlInfo)
25
25
  return urlInfo
26
26
  }
27
27
  const inferReference = (specifier, parentUrl) => {
28
- const parentUrlInfo = urlInfos[parentUrl]
28
+ const parentUrlInfo = getUrlInfo(parentUrl)
29
29
  if (!parentUrlInfo) {
30
30
  return null
31
31
  }
@@ -37,13 +37,13 @@ export const createUrlGraph = ({
37
37
  return firstReferenceOnThatUrl
38
38
  }
39
39
  const findDependent = (url, predicate) => {
40
- const urlInfo = urlInfos[url]
40
+ const urlInfo = getUrlInfo(url)
41
41
  if (!urlInfo) {
42
42
  return null
43
43
  }
44
44
  const visitDependents = (urlInfo) => {
45
45
  for (const dependentUrl of urlInfo.dependents) {
46
- const dependent = urlInfos[dependentUrl]
46
+ const dependent = getUrlInfo(dependentUrl)
47
47
  if (predicate(dependent)) {
48
48
  return dependent
49
49
  }
@@ -90,7 +90,7 @@ export const createUrlGraph = ({
90
90
  const removeDependencies = (urlInfo, urlsToPrune) => {
91
91
  urlsToPrune.forEach((urlToPrune) => {
92
92
  urlInfo.dependencies.delete(urlToPrune)
93
- const dependency = urlInfos[urlToPrune]
93
+ const dependency = getUrlInfo(urlToPrune)
94
94
  if (!dependency) {
95
95
  return
96
96
  }
@@ -122,7 +122,7 @@ export const createUrlGraph = ({
122
122
 
123
123
  if (clientFileChangeCallbackList) {
124
124
  clientFileChangeCallbackList.push(({ url }) => {
125
- const urlInfo = urlInfos[url]
125
+ const urlInfo = getUrlInfo(url)
126
126
  if (urlInfo) {
127
127
  considerModified(urlInfo, Date.now())
128
128
  }
@@ -139,12 +139,18 @@ export const createUrlGraph = ({
139
139
  urlInfo.modifiedTimestamp = modifiedTimestamp
140
140
  urlInfo.contentEtag = undefined
141
141
  urlInfo.dependents.forEach((dependentUrl) => {
142
- const dependentUrlInfo = urlInfos[dependentUrl]
142
+ const dependentUrlInfo = getUrlInfo(dependentUrl)
143
143
  const { hotAcceptDependencies = [] } = dependentUrlInfo.data
144
144
  if (!hotAcceptDependencies.includes(urlInfo.url)) {
145
145
  iterate(dependentUrlInfo)
146
146
  }
147
147
  })
148
+ urlInfo.dependencies.forEach((dependencyUrl) => {
149
+ const dependencyUrlInfo = getUrlInfo(dependencyUrl)
150
+ if (dependencyUrlInfo.isInline) {
151
+ iterate(dependencyUrlInfo)
152
+ }
153
+ })
148
154
  }
149
155
  iterate(urlInfo)
150
156
  }
@@ -164,7 +170,7 @@ export const createUrlGraph = ({
164
170
  }
165
171
 
166
172
  return {
167
- urlInfos,
173
+ urlInfoMap,
168
174
  reuseOrCreateUrlInfo,
169
175
  getUrlInfo,
170
176
  deleteUrlInfo,
@@ -174,12 +180,19 @@ export const createUrlGraph = ({
174
180
  considerModified,
175
181
  getRelatedUrlInfos,
176
182
 
183
+ toObject: () => {
184
+ const data = {}
185
+ urlInfoMap.forEach((urlInfo) => {
186
+ data[urlInfo.url] = urlInfo
187
+ })
188
+ return data
189
+ },
177
190
  toJSON: (rootDirectoryUrl) => {
178
191
  const data = {}
179
- Object.keys(urlInfos).forEach((url) => {
180
- const dependencyUrls = Array.from(urlInfos[url].dependencies)
192
+ urlInfoMap.forEach((urlInfo) => {
193
+ const dependencyUrls = Array.from(urlInfo.dependencies)
181
194
  if (dependencyUrls.length) {
182
- const relativeUrl = urlToRelativeUrl(url, rootDirectoryUrl)
195
+ const relativeUrl = urlToRelativeUrl(urlInfo.url, rootDirectoryUrl)
183
196
  data[relativeUrl] = dependencyUrls.map((dependencyUrl) =>
184
197
  urlToRelativeUrl(dependencyUrl, rootDirectoryUrl),
185
198
  )