@parcel/utils 2.0.0-nightly.151 → 2.0.0-nightly.1511

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.
Files changed (106) hide show
  1. package/.eslintrc.js +6 -6
  2. package/lib/index.js +37634 -318
  3. package/lib/index.js.map +1 -0
  4. package/package.json +47 -20
  5. package/src/DefaultMap.js +1 -1
  6. package/src/PromiseQueue.js +29 -12
  7. package/src/alternatives.js +145 -0
  8. package/src/ansi-html.js +2 -2
  9. package/src/blob.js +2 -1
  10. package/src/bundle-url.js +1 -1
  11. package/src/collection.js +35 -15
  12. package/src/config.js +132 -45
  13. package/src/countLines.js +5 -2
  14. package/src/debounce.js +1 -1
  15. package/src/dependency-location.js +11 -6
  16. package/src/generateBuildMetrics.js +158 -0
  17. package/src/generateCertificate.js +1 -1
  18. package/src/getCertificate.js +1 -1
  19. package/src/getExisting.js +1 -4
  20. package/src/getModuleParts.js +23 -0
  21. package/src/getRootDir.js +1 -2
  22. package/src/glob.js +51 -10
  23. package/src/hash.js +49 -0
  24. package/src/http-server.js +29 -19
  25. package/src/index.js +68 -22
  26. package/src/is-url.js +1 -1
  27. package/src/isDirectoryInside.js +11 -0
  28. package/src/openInBrowser.js +64 -0
  29. package/src/path.js +38 -6
  30. package/src/prettyDiagnostic.js +102 -30
  31. package/src/progress-message.js +22 -0
  32. package/src/relativeBundlePath.js +8 -13
  33. package/src/replaceBundleReferences.js +85 -41
  34. package/src/schema.js +100 -44
  35. package/src/shared-buffer.js +23 -0
  36. package/src/sourcemap.js +138 -0
  37. package/src/stream.js +31 -1
  38. package/src/urlJoin.js +3 -1
  39. package/test/DefaultMap.test.js +7 -4
  40. package/test/PromiseQueue.test.js +28 -0
  41. package/test/collection.test.js +13 -1
  42. package/test/config.test.js +98 -0
  43. package/test/input/config/.testrc +3 -0
  44. package/test/input/config/config.cjs +3 -0
  45. package/test/input/config/config.js +3 -0
  46. package/test/input/config/config.json +3 -0
  47. package/test/input/config/empty.json +0 -0
  48. package/test/input/config/empty.toml +0 -0
  49. package/test/input/sourcemap/referenced-min.js +2 -0
  50. package/test/input/sourcemap/referenced-min.js.map +6 -0
  51. package/test/input/sourcemap/source-root.js +2 -0
  52. package/test/input/sourcemap/source-root.js.map +7 -0
  53. package/test/replaceBundleReferences.test.js +268 -0
  54. package/test/sourcemap.test.js +207 -0
  55. package/test/throttle.test.js +1 -2
  56. package/test/urlJoin.test.js +37 -0
  57. package/lib/DefaultMap.js +0 -64
  58. package/lib/Deferred.js +0 -26
  59. package/lib/PromiseQueue.js +0 -133
  60. package/lib/TapStream.js +0 -41
  61. package/lib/ansi-html.js +0 -16
  62. package/lib/blob.js +0 -31
  63. package/lib/bundle-url.js +0 -43
  64. package/lib/collection.js +0 -62
  65. package/lib/config.js +0 -88
  66. package/lib/countLines.js +0 -18
  67. package/lib/debounce.js +0 -20
  68. package/lib/dependency-location.js +0 -21
  69. package/lib/escape-html.js +0 -24
  70. package/lib/escape-markdown.js +0 -15
  71. package/lib/generateBundleReport.js +0 -38
  72. package/lib/generateCertificate.js +0 -124
  73. package/lib/getCertificate.js +0 -19
  74. package/lib/getExisting.js +0 -23
  75. package/lib/getRootDir.js +0 -55
  76. package/lib/glob.js +0 -76
  77. package/lib/http-server.js +0 -64
  78. package/lib/is-url.js +0 -17
  79. package/lib/loadSourceMapUrl.js +0 -33
  80. package/lib/md5.js +0 -35
  81. package/lib/objectHash.js +0 -26
  82. package/lib/parseCSSImport.js +0 -16
  83. package/lib/path.js +0 -22
  84. package/lib/prettifyTime.js +0 -10
  85. package/lib/prettyDiagnostic.js +0 -57
  86. package/lib/promisify.js +0 -13
  87. package/lib/relativeBundlePath.js +0 -24
  88. package/lib/relativeUrl.js +0 -16
  89. package/lib/replaceBundleReferences.js +0 -151
  90. package/lib/resolve.js +0 -93
  91. package/lib/schema.js +0 -320
  92. package/lib/serializeObject.js +0 -28
  93. package/lib/stream.js +0 -51
  94. package/lib/throttle.js +0 -16
  95. package/lib/urlJoin.js +0 -27
  96. package/src/.babelrc +0 -3
  97. package/src/escape-markdown.js +0 -10
  98. package/src/generateBundleReport.js +0 -51
  99. package/src/loadSourceMapUrl.js +0 -33
  100. package/src/md5.js +0 -44
  101. package/src/promisify.js +0 -13
  102. package/src/resolve.js +0 -135
  103. package/src/serializeObject.js +0 -22
  104. package/test/escapeMarkdown.test.js +0 -29
  105. package/test/input/sourcemap/referenced.js +0 -7
  106. package/test/loadSourceMapUrl.test.js +0 -37
package/package.json CHANGED
@@ -1,11 +1,15 @@
1
1
  {
2
2
  "name": "@parcel/utils",
3
- "version": "2.0.0-nightly.151+1af603e1",
3
+ "version": "2.0.0-nightly.1511+2059029ee",
4
4
  "description": "Blazing fast, zero configuration web application bundler",
5
5
  "license": "MIT",
6
6
  "publishConfig": {
7
7
  "access": "public"
8
8
  },
9
+ "funding": {
10
+ "type": "opencollective",
11
+ "url": "https://opencollective.com/parcel"
12
+ },
9
13
  "repository": {
10
14
  "type": "git",
11
15
  "url": "https://github.com/parcel-bundler/parcel.git"
@@ -13,32 +17,55 @@
13
17
  "main": "lib/index.js",
14
18
  "source": "src/index.js",
15
19
  "engines": {
16
- "node": ">= 10.0.0"
20
+ "node": ">= 12.0.0"
21
+ },
22
+ "targets": {
23
+ "main": {
24
+ "includeNodeModules": {
25
+ "@parcel/codeframe": false,
26
+ "@parcel/diagnostic": false,
27
+ "@parcel/rust": false,
28
+ "@parcel/logger": false,
29
+ "@parcel/markdown-ansi": false,
30
+ "@parcel/source-map": false,
31
+ "chalk": false
32
+ }
33
+ }
17
34
  },
18
35
  "dependencies": {
36
+ "@parcel/codeframe": "2.0.0-nightly.1511+2059029ee",
37
+ "@parcel/diagnostic": "2.0.0-nightly.1511+2059029ee",
38
+ "@parcel/logger": "2.0.0-nightly.1511+2059029ee",
39
+ "@parcel/markdown-ansi": "2.0.0-nightly.1511+2059029ee",
40
+ "@parcel/rust": "2.12.1-nightly.3134+2059029ee",
41
+ "@parcel/source-map": "^2.1.1",
42
+ "chalk": "^4.1.0",
43
+ "nullthrows": "^1.1.1"
44
+ },
45
+ "devDependencies": {
19
46
  "@iarna/toml": "^2.2.0",
20
- "@parcel/codeframe": "2.0.0-nightly.151+1af603e1",
21
- "@parcel/diagnostic": "2.0.0-nightly.151+1af603e1",
22
- "@parcel/logger": "2.0.0-nightly.151+1af603e1",
23
- "@parcel/markdown-ansi": "2.0.0-nightly.151+1af603e1",
24
- "ansi-html": "^0.0.7",
25
- "chalk": "^2.4.2",
47
+ "ansi-html-community": "0.0.8",
26
48
  "clone": "^2.1.1",
27
- "fast-glob": "3.1.1",
49
+ "fast-glob": "^3.2.12",
50
+ "fastest-levenshtein": "^1.0.16",
28
51
  "is-glob": "^4.0.0",
29
52
  "is-url": "^1.2.2",
30
- "js-levenshtein": "^1.1.6",
31
- "json5": "^1.0.1",
32
- "micromatch": "^4.0.2",
33
- "node-forge": "^0.8.1",
53
+ "json5": "^2.2.0",
54
+ "lru-cache": "^6.0.0",
55
+ "micromatch": "^4.0.4",
56
+ "node-forge": "^1.2.1",
34
57
  "nullthrows": "^1.1.1",
35
- "resolve": "^1.12.0",
36
- "serialize-to-js": "^3.0.1",
37
- "terser": "^3.7.3"
58
+ "open": "^7.0.3",
59
+ "random-int": "^1.0.0",
60
+ "snarkdown": "^2.0.0",
61
+ "strip-ansi": "^6.0.0",
62
+ "terminal-link": "^2.1.1"
38
63
  },
39
- "devDependencies": {
40
- "@babel/plugin-transform-flow-strip-types": "^7.2.0",
41
- "random-int": "^1.0.0"
64
+ "browser": {
65
+ "./src/generateCertificate.js": false,
66
+ "./src/http-server.js": false,
67
+ "./src/openInBrowser.js": false,
68
+ "@parcel/markdown-ansi": false
42
69
  },
43
- "gitHead": "1af603e1cfab014585ded8aec4d3dcdb7e685987"
70
+ "gitHead": "2059029ee91e5f03a273b0954d3e629d7375f986"
44
71
  }
package/src/DefaultMap.js CHANGED
@@ -24,7 +24,7 @@ export class DefaultMap<K, V> extends Map<K, V> {
24
24
 
25
25
  // Duplicated from DefaultMap implementation for Flow
26
26
  // Roughly mirrors https://github.com/facebook/flow/blob/2eb5a78d92c167117ba9caae070afd2b9f598599/lib/core.js#L617
27
- export class DefaultWeakMap<K: {...}, V> extends WeakMap<K, V> {
27
+ export class DefaultWeakMap<K: interface {}, V> extends WeakMap<K, V> {
28
28
  _getDefault: K => V;
29
29
 
30
30
  constructor(getDefault: K => V, entries?: Iterable<[K, V]>) {
@@ -10,8 +10,10 @@ export default class PromiseQueue<T> {
10
10
  _numRunning: number = 0;
11
11
  _queue: Array<() => Promise<void>> = [];
12
12
  _runPromise: ?Promise<Array<T>> = null;
13
+ _error: mixed;
13
14
  _count: number = 0;
14
15
  _results: Array<T> = [];
16
+ _addSubscriptions: Set<() => void> = new Set();
15
17
 
16
18
  constructor(opts: PromiseQueueOpts = {maxConcurrent: Infinity}) {
17
19
  if (opts.maxConcurrent <= 0) {
@@ -42,12 +44,24 @@ export default class PromiseQueue<T> {
42
44
 
43
45
  this._queue.push(wrapped);
44
46
 
47
+ for (const addFn of this._addSubscriptions) {
48
+ addFn();
49
+ }
50
+
45
51
  if (this._numRunning > 0 && this._numRunning < this._maxConcurrent) {
46
52
  this._next();
47
53
  }
48
54
  });
49
55
  }
50
56
 
57
+ subscribeToAdd(fn: () => void): () => void {
58
+ this._addSubscriptions.add(fn);
59
+
60
+ return () => {
61
+ this._addSubscriptions.delete(fn);
62
+ };
63
+ }
64
+
51
65
  run(): Promise<Array<T>> {
52
66
  if (this._runPromise != null) {
53
67
  return this._runPromise;
@@ -74,7 +88,7 @@ export default class PromiseQueue<T> {
74
88
  if (this._queue.length) {
75
89
  this._next();
76
90
  } else if (this._numRunning === 0) {
77
- this._resolve();
91
+ this._done();
78
92
  }
79
93
  }
80
94
 
@@ -82,10 +96,15 @@ export default class PromiseQueue<T> {
82
96
  this._numRunning++;
83
97
  try {
84
98
  await fn();
85
- this._numRunning--;
86
99
  } catch (e) {
87
- this._reject(e);
88
- // rejecting resets state so numRunning is reset to 0 here
100
+ // Only store the first error that occurs.
101
+ // We don't reject immediately so that any other concurrent
102
+ // requests have time to complete.
103
+ if (this._error == null) {
104
+ this._error = e;
105
+ }
106
+ } finally {
107
+ this._numRunning--;
89
108
  }
90
109
  }
91
110
 
@@ -98,17 +117,15 @@ export default class PromiseQueue<T> {
98
117
  this._deferred = null;
99
118
  }
100
119
 
101
- _reject(err: mixed): void {
120
+ _done(): void {
102
121
  if (this._deferred != null) {
103
- this._deferred.reject(err);
122
+ if (this._error != null) {
123
+ this._deferred.reject(this._error);
124
+ } else {
125
+ this._deferred.resolve(this._results);
126
+ }
104
127
  }
105
- this._resetState();
106
- }
107
128
 
108
- _resolve(): void {
109
- if (this._deferred != null) {
110
- this._deferred.resolve(this._results);
111
- }
112
129
  this._resetState();
113
130
  }
114
131
  }
@@ -0,0 +1,145 @@
1
+ // @flow
2
+ import path from 'path';
3
+ import type {FileSystem} from '@parcel/fs';
4
+ import {fuzzySearch} from './schema';
5
+ import {relativePath} from './path';
6
+ import {resolveConfig} from './config';
7
+
8
+ export async function findAlternativeNodeModules(
9
+ fs: FileSystem,
10
+ moduleName: string,
11
+ dir: string,
12
+ ): Promise<Array<string>> {
13
+ let potentialModules: Array<string> = [];
14
+ let root = path.parse(dir).root;
15
+ let isOrganisationModule = moduleName.startsWith('@');
16
+
17
+ while (dir !== root) {
18
+ // Skip node_modules directories
19
+ if (path.basename(dir) === 'node_modules') {
20
+ dir = path.dirname(dir);
21
+ }
22
+
23
+ try {
24
+ let modulesDir = path.join(dir, 'node_modules');
25
+ let stats = await fs.stat(modulesDir);
26
+ if (stats.isDirectory()) {
27
+ let dirContent = (await fs.readdir(modulesDir)).sort();
28
+
29
+ // Filter out the modules that interest us
30
+ let modules = dirContent.filter(i =>
31
+ isOrganisationModule ? i.startsWith('@') : !i.startsWith('@'),
32
+ );
33
+
34
+ // If it's an organisation module, loop through all the modules of that organisation
35
+ if (isOrganisationModule) {
36
+ await Promise.all(
37
+ modules.map(async item => {
38
+ let orgDirPath = path.join(modulesDir, item);
39
+ let orgDirContent = (await fs.readdir(orgDirPath)).sort();
40
+
41
+ // Add all org packages
42
+ potentialModules.push(...orgDirContent.map(i => `${item}/${i}`));
43
+ }),
44
+ );
45
+ } else {
46
+ potentialModules.push(...modules);
47
+ }
48
+ }
49
+ } catch (err) {
50
+ // ignore
51
+ }
52
+
53
+ // Move up a directory
54
+ dir = path.dirname(dir);
55
+ }
56
+
57
+ return fuzzySearch(potentialModules.sort(), moduleName).slice(0, 2);
58
+ }
59
+
60
+ async function findAllFilesUp({
61
+ fs,
62
+ dir,
63
+ root,
64
+ basedir,
65
+ maxlength,
66
+ collected,
67
+ leadingDotSlash = true,
68
+ includeDirectories = true,
69
+ }: {|
70
+ fs: FileSystem,
71
+ dir: string,
72
+ root: string,
73
+ basedir: string,
74
+ maxlength: number,
75
+ collected: Array<string>,
76
+ leadingDotSlash?: boolean,
77
+ includeDirectories?: boolean,
78
+ |}): Promise<mixed> {
79
+ let dirContent = (await fs.readdir(dir)).sort();
80
+ return Promise.all(
81
+ dirContent.map(async item => {
82
+ let fullPath = path.join(dir, item);
83
+ let relativeFilePath = relativePath(basedir, fullPath, leadingDotSlash);
84
+ if (relativeFilePath.length < maxlength) {
85
+ let stats = await fs.stat(fullPath);
86
+ let isDir = stats.isDirectory();
87
+ if ((isDir && includeDirectories) || stats.isFile()) {
88
+ collected.push(relativeFilePath);
89
+ }
90
+
91
+ // If it's a directory, run over each item within said directory...
92
+ if (isDir) {
93
+ return findAllFilesUp({
94
+ fs,
95
+ dir: fullPath,
96
+ root,
97
+ basedir,
98
+ maxlength,
99
+ collected,
100
+ });
101
+ }
102
+ }
103
+ }),
104
+ );
105
+ }
106
+
107
+ export async function findAlternativeFiles(
108
+ fs: FileSystem,
109
+ fileSpecifier: string,
110
+ dir: string,
111
+ projectRoot: string,
112
+ leadingDotSlash?: boolean = true,
113
+ includeDirectories?: boolean = true,
114
+ includeExtension?: boolean = false,
115
+ ): Promise<Array<string>> {
116
+ let potentialFiles: Array<string> = [];
117
+ // Find our root, we won't recommend files above the package root as that's bad practise
118
+ let pkg = await resolveConfig(
119
+ fs,
120
+ path.join(dir, 'index'),
121
+ ['package.json'],
122
+ projectRoot,
123
+ );
124
+
125
+ let pkgRoot = pkg ? path.dirname(pkg) : projectRoot;
126
+ await findAllFilesUp({
127
+ fs,
128
+ dir: pkgRoot,
129
+ root: pkgRoot,
130
+ basedir: dir,
131
+ maxlength: fileSpecifier.length + 10,
132
+ collected: potentialFiles,
133
+ leadingDotSlash,
134
+ includeDirectories,
135
+ });
136
+
137
+ if (path.extname(fileSpecifier) === '' && !includeExtension) {
138
+ potentialFiles = potentialFiles.map(p => {
139
+ let ext = path.extname(p);
140
+ return ext.length > 0 ? p.slice(0, -ext.length) : p;
141
+ });
142
+ }
143
+
144
+ return fuzzySearch(potentialFiles, fileSpecifier).slice(0, 2);
145
+ }
package/src/ansi-html.js CHANGED
@@ -1,5 +1,5 @@
1
- // @flow
2
- import ansiHTML from 'ansi-html';
1
+ // @flow strict-local
2
+ import ansiHTML from 'ansi-html-community';
3
3
  import {escapeHTML} from './escape-html';
4
4
 
5
5
  export function ansiHtml(ansi: string): string {
package/src/blob.js CHANGED
@@ -2,7 +2,8 @@
2
2
 
3
3
  import type {Blob} from '@parcel/types';
4
4
 
5
- import {bufferStream} from '../';
5
+ import {Buffer} from 'buffer';
6
+ import {bufferStream} from './';
6
7
  import {Readable} from 'stream';
7
8
 
8
9
  export function blobToBuffer(blob: Blob): Promise<Buffer> {
package/src/bundle-url.js CHANGED
@@ -1,7 +1,7 @@
1
1
  // @flow strict-local
2
2
 
3
3
  let bundleURL: ?string = null;
4
- function getBundleURLCached() {
4
+ function getBundleURLCached(): string {
5
5
  if (bundleURL == null) {
6
6
  bundleURL = _getBundleURL();
7
7
  }
package/src/collection.js CHANGED
@@ -4,28 +4,16 @@ export function unique<T>(array: Array<T>): Array<T> {
4
4
  return [...new Set(array)];
5
5
  }
6
6
 
7
- export function flatMap<T, U>(
8
- array: Array<T>,
9
- projectFn: (T, number, Array<T>) => Array<U>,
10
- ): Array<U> {
11
- let out = [];
12
-
13
- for (let arr of array.map(projectFn)) {
14
- out.push(...arr);
15
- }
16
- return out;
17
- }
18
-
19
7
  export function objectSortedEntries(obj: {
20
8
  +[string]: mixed,
21
- ...,
9
+ ...
22
10
  }): Array<[string, mixed]> {
23
11
  return Object.entries(obj).sort(([keyA], [keyB]) => keyA.localeCompare(keyB));
24
12
  }
25
13
 
26
14
  export function objectSortedEntriesDeep(object: {
27
15
  +[string]: mixed,
28
- ...,
16
+ ...
29
17
  }): Array<[string, mixed]> {
30
18
  let sortedEntries = objectSortedEntries(object);
31
19
  for (let i = 0; i < sortedEntries.length; i++) {
@@ -46,12 +34,44 @@ function sortEntry(entry: mixed) {
46
34
  return entry;
47
35
  }
48
36
 
49
- export function setDifference<T>(a: Set<T>, b: Set<T>): Set<T> {
37
+ export function setDifference<T>(
38
+ a: $ReadOnlySet<T>,
39
+ b: $ReadOnlySet<T>,
40
+ ): Set<T> {
50
41
  let difference = new Set();
51
42
  for (let e of a) {
52
43
  if (!b.has(e)) {
53
44
  difference.add(e);
54
45
  }
55
46
  }
47
+ for (let d of b) {
48
+ if (!a.has(d)) {
49
+ difference.add(d);
50
+ }
51
+ }
56
52
  return difference;
57
53
  }
54
+
55
+ export function setIntersect<T>(a: Set<T>, b: $ReadOnlySet<T>): void {
56
+ for (let entry of a) {
57
+ if (!b.has(entry)) {
58
+ a.delete(entry);
59
+ }
60
+ }
61
+ }
62
+
63
+ export function setUnion<T>(a: Iterable<T>, b: Iterable<T>): Set<T> {
64
+ return new Set([...a, ...b]);
65
+ }
66
+
67
+ export function setEqual<T>(a: $ReadOnlySet<T>, b: $ReadOnlySet<T>): boolean {
68
+ if (a.size != b.size) {
69
+ return false;
70
+ }
71
+ for (let entry of a) {
72
+ if (!b.has(entry)) {
73
+ return false;
74
+ }
75
+ }
76
+ return true;
77
+ }
package/src/config.js CHANGED
@@ -2,87 +2,89 @@
2
2
 
3
3
  import type {ConfigResult, File, FilePath} from '@parcel/types';
4
4
  import type {FileSystem} from '@parcel/fs';
5
+ import ThrowableDiagnostic from '@parcel/diagnostic';
5
6
  import path from 'path';
6
7
  import clone from 'clone';
8
+ import json5 from 'json5';
9
+ import {parse as toml} from '@iarna/toml';
10
+ import LRU from 'lru-cache';
7
11
 
8
- type ConfigOutput = {|
12
+ export type ConfigOutput = {|
9
13
  config: ConfigResult,
10
14
  files: Array<File>,
11
15
  |};
12
16
 
13
- type ConfigOptions = {|
17
+ export type ConfigOptions = {|
14
18
  parse?: boolean,
19
+ parser?: string => any,
15
20
  |};
16
21
 
17
- const PARSERS = {
18
- json: require('json5').parse,
19
- toml: require('@iarna/toml').parse,
20
- };
21
-
22
- const existsCache = new Map();
22
+ const configCache = new LRU<FilePath, ConfigOutput>({max: 500});
23
+ const resolveCache = new Map();
23
24
 
24
- export async function resolveConfig(
25
+ export function resolveConfig(
25
26
  fs: FileSystem,
26
27
  filepath: FilePath,
27
28
  filenames: Array<FilePath>,
28
- opts: ?ConfigOptions,
29
- root: FilePath = path.parse(filepath).root,
30
- ): Promise<FilePath | null> {
31
- filepath = await fs.realpath(path.dirname(filepath));
32
-
33
- // Don't traverse above the module root
34
- if (filepath === root || path.basename(filepath) === 'node_modules') {
35
- return null;
29
+ projectRoot: FilePath,
30
+ ): Promise<?FilePath> {
31
+ // Cache the result of resolving config for this directory.
32
+ // This is automatically invalidated at the end of the current build.
33
+ let key = path.dirname(filepath) + filenames.join(',');
34
+ let cached = resolveCache.get(key);
35
+ if (cached !== undefined) {
36
+ return Promise.resolve(cached);
36
37
  }
37
38
 
38
- for (const filename of filenames) {
39
- let file = path.join(filepath, filename);
40
- if ((await fs.exists(file)) && (await fs.stat(file)).isFile()) {
41
- return file;
42
- }
43
- }
39
+ let resolved = fs.findAncestorFile(
40
+ filenames,
41
+ path.dirname(filepath),
42
+ projectRoot,
43
+ );
44
+ resolveCache.set(key, resolved);
45
+ return Promise.resolve(resolved);
46
+ }
44
47
 
45
- return resolveConfig(fs, filepath, filenames, opts);
48
+ export function resolveConfigSync(
49
+ fs: FileSystem,
50
+ filepath: FilePath,
51
+ filenames: Array<FilePath>,
52
+ projectRoot: FilePath,
53
+ ): ?FilePath {
54
+ return fs.findAncestorFile(filenames, path.dirname(filepath), projectRoot);
46
55
  }
47
56
 
48
57
  export async function loadConfig(
49
58
  fs: FileSystem,
50
59
  filepath: FilePath,
51
60
  filenames: Array<FilePath>,
61
+ projectRoot: FilePath,
52
62
  opts: ?ConfigOptions,
53
63
  ): Promise<ConfigOutput | null> {
54
- let configFile = await resolveConfig(fs, filepath, filenames, opts);
64
+ let parse = opts?.parse ?? true;
65
+ let configFile = await resolveConfig(fs, filepath, filenames, projectRoot);
55
66
  if (configFile) {
67
+ let cachedOutput = configCache.get(String(parse) + configFile);
68
+ if (cachedOutput) {
69
+ return cachedOutput;
70
+ }
71
+
56
72
  try {
57
73
  let extname = path.extname(configFile).slice(1);
58
- if (extname === 'js') {
59
- return {
74
+ if (extname === 'js' || extname === 'cjs') {
75
+ let output = {
60
76
  // $FlowFixMe
61
- config: clone(require(configFile)),
77
+ config: clone(module.require(configFile)),
62
78
  files: [{filePath: configFile}],
63
79
  };
64
- }
65
-
66
- let configContent = await fs.readFile(configFile, 'utf8');
67
- if (!configContent) {
68
- return null;
69
- }
70
80
 
71
- let config;
72
- if (opts && opts.parse === false) {
73
- config = configContent;
74
- } else {
75
- let parse = PARSERS[extname] || PARSERS.json;
76
- config = parse(configContent);
81
+ configCache.set(configFile, output);
82
+ return output;
77
83
  }
78
84
 
79
- return {
80
- config: config,
81
- files: [{filePath: configFile}],
82
- };
85
+ return readConfig(fs, configFile, opts);
83
86
  } catch (err) {
84
87
  if (err.code === 'MODULE_NOT_FOUND' || err.code === 'ENOENT') {
85
- existsCache.delete(configFile);
86
88
  return null;
87
89
  }
88
90
 
@@ -92,3 +94,88 @@ export async function loadConfig(
92
94
 
93
95
  return null;
94
96
  }
97
+
98
+ loadConfig.clear = () => {
99
+ configCache.reset();
100
+ resolveCache.clear();
101
+ };
102
+
103
+ export async function readConfig(
104
+ fs: FileSystem,
105
+ configFile: FilePath,
106
+ opts: ?ConfigOptions,
107
+ ): Promise<ConfigOutput | null> {
108
+ let parse = opts?.parse ?? true;
109
+ let cachedOutput = configCache.get(String(parse) + configFile);
110
+ if (cachedOutput) {
111
+ return cachedOutput;
112
+ }
113
+
114
+ try {
115
+ let configContent = await fs.readFile(configFile, 'utf8');
116
+ let config;
117
+ if (parse === false) {
118
+ config = configContent;
119
+ } else {
120
+ let extname = path.extname(configFile).slice(1);
121
+ let parse = opts?.parser ?? getParser(extname);
122
+ try {
123
+ config = parse(configContent);
124
+ } catch (e) {
125
+ if (extname !== '' && extname !== 'json') {
126
+ throw e;
127
+ }
128
+
129
+ let pos = {
130
+ line: e.lineNumber,
131
+ column: e.columnNumber,
132
+ };
133
+
134
+ throw new ThrowableDiagnostic({
135
+ diagnostic: {
136
+ message: `Failed to parse ${path.basename(configFile)}`,
137
+ origin: '@parcel/utils',
138
+ codeFrames: [
139
+ {
140
+ language: 'json5',
141
+ filePath: configFile,
142
+ code: configContent,
143
+ codeHighlights: [
144
+ {
145
+ start: pos,
146
+ end: pos,
147
+ message: e.message,
148
+ },
149
+ ],
150
+ },
151
+ ],
152
+ },
153
+ });
154
+ }
155
+ }
156
+
157
+ let output = {
158
+ config,
159
+ files: [{filePath: configFile}],
160
+ };
161
+
162
+ configCache.set(String(parse) + configFile, output);
163
+ return output;
164
+ } catch (err) {
165
+ if (err.code === 'MODULE_NOT_FOUND' || err.code === 'ENOENT') {
166
+ return null;
167
+ }
168
+
169
+ throw err;
170
+ }
171
+ }
172
+
173
+ function getParser(extname) {
174
+ switch (extname) {
175
+ case 'toml':
176
+ return toml;
177
+ case 'json':
178
+ default:
179
+ return json5.parse;
180
+ }
181
+ }
package/src/countLines.js CHANGED
@@ -1,8 +1,11 @@
1
1
  // @flow strict-local
2
2
 
3
- export default function countLines(string: string): number {
3
+ export default function countLines(
4
+ string: string,
5
+ startIndex: number = 0,
6
+ ): number {
4
7
  let lines = 1;
5
- for (let i = 0; i < string.length; i++) {
8
+ for (let i = startIndex; i < string.length; i++) {
6
9
  if (string.charAt(i) === '\n') {
7
10
  lines++;
8
11
  }
package/src/debounce.js CHANGED
@@ -6,7 +6,7 @@ export default function debounce<TArgs: Array<mixed>>(
6
6
  ): (...args: TArgs) => void {
7
7
  let timeout;
8
8
 
9
- return function(...args: TArgs) {
9
+ return function (...args: TArgs) {
10
10
  if (timeout) {
11
11
  clearTimeout(timeout);
12
12
  }