@parcel/utils 2.0.0-nightly.92 → 2.0.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.
Files changed (93) hide show
  1. package/.eslintrc.js +6 -6
  2. package/lib/DefaultMap.js +26 -5
  3. package/lib/Deferred.js +10 -2
  4. package/lib/PromiseQueue.js +21 -30
  5. package/lib/TapStream.js +10 -10
  6. package/lib/alternatives.js +134 -0
  7. package/lib/ansi-html.js +10 -2
  8. package/lib/blob.js +14 -6
  9. package/lib/collection.js +0 -11
  10. package/lib/config.js +107 -34
  11. package/lib/countLines.js +2 -2
  12. package/lib/dependency-location.js +3 -3
  13. package/lib/generateBuildMetrics.js +148 -0
  14. package/lib/generateCertificate.js +33 -8
  15. package/lib/getExisting.js +11 -3
  16. package/lib/getRootDir.js +18 -7
  17. package/lib/glob.js +53 -19
  18. package/lib/hash.js +44 -0
  19. package/lib/http-server.js +48 -10
  20. package/lib/index.js +280 -222
  21. package/lib/is-url.js +12 -2
  22. package/lib/isDirectoryInside.js +24 -0
  23. package/lib/objectHash.js +10 -2
  24. package/lib/openInBrowser.js +94 -0
  25. package/lib/path.js +33 -6
  26. package/lib/prettyDiagnostic.js +107 -25
  27. package/lib/relativeBundlePath.js +13 -7
  28. package/lib/relativeUrl.js +19 -3
  29. package/lib/replaceBundleReferences.js +91 -35
  30. package/lib/schema.js +104 -33
  31. package/lib/sourcemap.js +147 -0
  32. package/lib/stream.js +38 -3
  33. package/lib/urlJoin.js +25 -6
  34. package/package.json +22 -16
  35. package/src/DefaultMap.js +25 -1
  36. package/src/PromiseQueue.js +16 -12
  37. package/src/alternatives.js +143 -0
  38. package/src/ansi-html.js +2 -2
  39. package/src/blob.js +3 -3
  40. package/src/bundle-url.js +1 -1
  41. package/src/collection.js +2 -14
  42. package/src/config.js +67 -34
  43. package/src/countLines.js +5 -2
  44. package/src/debounce.js +1 -1
  45. package/src/dependency-location.js +11 -6
  46. package/src/generateBuildMetrics.js +158 -0
  47. package/src/generateCertificate.js +1 -1
  48. package/src/getCertificate.js +1 -1
  49. package/src/getExisting.js +1 -4
  50. package/src/getRootDir.js +1 -2
  51. package/src/glob.js +29 -11
  52. package/src/hash.js +34 -0
  53. package/src/http-server.js +10 -12
  54. package/src/index.js +49 -23
  55. package/src/is-url.js +1 -1
  56. package/src/isDirectoryInside.js +11 -0
  57. package/src/openInBrowser.js +64 -0
  58. package/src/path.js +38 -6
  59. package/src/prettyDiagnostic.js +59 -28
  60. package/src/relativeBundlePath.js +8 -13
  61. package/src/replaceBundleReferences.js +75 -39
  62. package/src/schema.js +101 -44
  63. package/src/sourcemap.js +135 -0
  64. package/src/stream.js +31 -1
  65. package/src/urlJoin.js +3 -1
  66. package/test/DefaultMap.test.js +8 -5
  67. package/test/input/sourcemap/referenced-min.js +2 -0
  68. package/test/input/sourcemap/referenced-min.js.map +6 -0
  69. package/test/input/sourcemap/source-root.js +2 -0
  70. package/test/input/sourcemap/source-root.js.map +7 -0
  71. package/test/objectHash.test.js +33 -0
  72. package/test/prettifyTime.test.js +17 -0
  73. package/test/replaceBundleReferences.test.js +268 -0
  74. package/test/sourcemap.test.js +207 -0
  75. package/test/throttle.test.js +1 -2
  76. package/test/urlJoin.test.js +37 -0
  77. package/lib/generateBundleReport.js +0 -38
  78. package/lib/loadSourceMapUrl.js +0 -33
  79. package/lib/md5.js +0 -35
  80. package/lib/prettyError.js +0 -43
  81. package/lib/promisify.js +0 -13
  82. package/lib/resolve.js +0 -93
  83. package/lib/serializeObject.js +0 -28
  84. package/src/generateBundleReport.js +0 -51
  85. package/src/loadSourceMapUrl.js +0 -33
  86. package/src/md5.js +0 -44
  87. package/src/prettyError.js +0 -54
  88. package/src/promisify.js +0 -13
  89. package/src/resolve.js +0 -123
  90. package/src/serializeObject.js +0 -22
  91. package/test/input/sourcemap/referenced.js +0 -7
  92. package/test/loadSourceMapUrl.test.js +0 -37
  93. package/test/prettyError.test.js +0 -104
@@ -0,0 +1,143 @@
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
+ }
46
+ }
47
+ } catch (err) {
48
+ // ignore
49
+ }
50
+
51
+ // Move up a directory
52
+ dir = path.dirname(dir);
53
+ }
54
+
55
+ return fuzzySearch(potentialModules.sort(), moduleName).slice(0, 2);
56
+ }
57
+
58
+ async function findAllFilesUp({
59
+ fs,
60
+ dir,
61
+ root,
62
+ basedir,
63
+ maxlength,
64
+ collected,
65
+ leadingDotSlash = true,
66
+ includeDirectories = true,
67
+ }: {|
68
+ fs: FileSystem,
69
+ dir: string,
70
+ root: string,
71
+ basedir: string,
72
+ maxlength: number,
73
+ collected: Array<string>,
74
+ leadingDotSlash?: boolean,
75
+ includeDirectories?: boolean,
76
+ |}): Promise<mixed> {
77
+ let dirContent = (await fs.readdir(dir)).sort();
78
+ return Promise.all(
79
+ dirContent.map(async item => {
80
+ let fullPath = path.join(dir, item);
81
+ let relativeFilePath = relativePath(basedir, fullPath, leadingDotSlash);
82
+ if (relativeFilePath.length < maxlength) {
83
+ let stats = await fs.stat(fullPath);
84
+ let isDir = stats.isDirectory();
85
+ if ((isDir && includeDirectories) || stats.isFile()) {
86
+ collected.push(relativeFilePath);
87
+ }
88
+
89
+ // If it's a directory, run over each item within said directory...
90
+ if (isDir) {
91
+ return findAllFilesUp({
92
+ fs,
93
+ dir: fullPath,
94
+ root,
95
+ basedir,
96
+ maxlength,
97
+ collected,
98
+ });
99
+ }
100
+ }
101
+ }),
102
+ );
103
+ }
104
+
105
+ export async function findAlternativeFiles(
106
+ fs: FileSystem,
107
+ fileSpecifier: string,
108
+ dir: string,
109
+ projectRoot: string,
110
+ leadingDotSlash?: boolean = true,
111
+ includeDirectories?: boolean = true,
112
+ includeExtension?: boolean = false,
113
+ ): Promise<Array<string>> {
114
+ let potentialFiles: Array<string> = [];
115
+ // Find our root, we won't recommend files above the package root as that's bad practise
116
+ let pkg = await resolveConfig(
117
+ fs,
118
+ path.join(dir, 'index'),
119
+ ['package.json'],
120
+ projectRoot,
121
+ );
122
+
123
+ let pkgRoot = pkg ? path.dirname(pkg) : projectRoot;
124
+ await findAllFilesUp({
125
+ fs,
126
+ dir: pkgRoot,
127
+ root: pkgRoot,
128
+ basedir: dir,
129
+ maxlength: fileSpecifier.length + 10,
130
+ collected: potentialFiles,
131
+ leadingDotSlash,
132
+ includeDirectories,
133
+ });
134
+
135
+ if (path.extname(fileSpecifier) === '' && !includeExtension) {
136
+ potentialFiles = potentialFiles.map(p => {
137
+ let ext = path.extname(p);
138
+ return ext.length > 0 ? p.slice(0, -ext.length) : p;
139
+ });
140
+ }
141
+
142
+ return fuzzySearch(potentialFiles, fileSpecifier).slice(0, 2);
143
+ }
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,16 +2,16 @@
2
2
 
3
3
  import type {Blob} from '@parcel/types';
4
4
 
5
- import {bufferStream} from '../';
5
+ import {bufferStream} from './';
6
6
  import {Readable} from 'stream';
7
7
 
8
8
  export function blobToBuffer(blob: Blob): Promise<Buffer> {
9
9
  if (blob instanceof Readable) {
10
10
  return bufferStream(blob);
11
11
  } else if (blob instanceof Buffer) {
12
- return Buffer.from(blob);
12
+ return Promise.resolve(Buffer.from(blob));
13
13
  } else {
14
- return Buffer.from(blob, 'utf8');
14
+ return Promise.resolve(Buffer.from(blob, 'utf8'));
15
15
  }
16
16
  }
17
17
 
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++) {
package/src/config.js CHANGED
@@ -4,85 +4,103 @@ import type {ConfigResult, File, FilePath} from '@parcel/types';
4
4
  import type {FileSystem} from '@parcel/fs';
5
5
  import path from 'path';
6
6
  import clone from 'clone';
7
+ import {parse as json5} from 'json5';
8
+ import {parse as toml} from '@iarna/toml';
9
+ import LRU from 'lru-cache';
7
10
 
8
- type ConfigOutput = {|
11
+ export type ConfigOutput = {|
9
12
  config: ConfigResult,
10
13
  files: Array<File>,
11
14
  |};
12
15
 
13
- type ConfigOptions = {|
16
+ export type ConfigOptions = {|
14
17
  parse?: boolean,
18
+ parser?: string => any,
15
19
  |};
16
20
 
17
- const PARSERS = {
18
- json: require('json5').parse,
19
- toml: require('@iarna/toml').parse,
20
- };
21
-
22
- const existsCache = new Map();
21
+ const configCache = new LRU<FilePath, ConfigOutput>({max: 500});
22
+ const resolveCache = new Map();
23
23
 
24
- export async function resolveConfig(
24
+ export function resolveConfig(
25
25
  fs: FileSystem,
26
26
  filepath: FilePath,
27
27
  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;
28
+ projectRoot: FilePath,
29
+ ): Promise<?FilePath> {
30
+ // Cache the result of resolving config for this directory.
31
+ // This is automatically invalidated at the end of the current build.
32
+ let key = path.dirname(filepath) + filenames.join(',');
33
+ let cached = resolveCache.get(key);
34
+ if (cached !== undefined) {
35
+ return Promise.resolve(cached);
36
36
  }
37
37
 
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
- }
38
+ let resolved = fs.findAncestorFile(
39
+ filenames,
40
+ path.dirname(filepath),
41
+ projectRoot,
42
+ );
43
+ resolveCache.set(key, resolved);
44
+ return Promise.resolve(resolved);
45
+ }
44
46
 
45
- return resolveConfig(fs, filepath, filenames, opts);
47
+ export function resolveConfigSync(
48
+ fs: FileSystem,
49
+ filepath: FilePath,
50
+ filenames: Array<FilePath>,
51
+ projectRoot: FilePath,
52
+ ): ?FilePath {
53
+ return fs.findAncestorFile(filenames, path.dirname(filepath), projectRoot);
46
54
  }
47
55
 
48
56
  export async function loadConfig(
49
57
  fs: FileSystem,
50
58
  filepath: FilePath,
51
59
  filenames: Array<FilePath>,
60
+ projectRoot: FilePath,
52
61
  opts: ?ConfigOptions,
53
62
  ): Promise<ConfigOutput | null> {
54
- let configFile = await resolveConfig(fs, filepath, filenames, opts);
63
+ let parse = opts?.parse ?? true;
64
+ let configFile = await resolveConfig(fs, filepath, filenames, projectRoot);
55
65
  if (configFile) {
66
+ let cachedOutput = configCache.get(String(parse) + configFile);
67
+ if (cachedOutput) {
68
+ return cachedOutput;
69
+ }
70
+
56
71
  try {
57
72
  let extname = path.extname(configFile).slice(1);
58
73
  if (extname === 'js') {
59
- return {
74
+ let output = {
60
75
  // $FlowFixMe
61
76
  config: clone(require(configFile)),
62
77
  files: [{filePath: configFile}],
63
78
  };
79
+
80
+ configCache.set(configFile, output);
81
+ return output;
64
82
  }
65
83
 
66
84
  let configContent = await fs.readFile(configFile, 'utf8');
67
- if (!configContent) {
68
- return null;
69
- }
85
+ if (!configContent) return null;
70
86
 
71
87
  let config;
72
- if (opts && opts.parse === false) {
88
+ if (parse === false) {
73
89
  config = configContent;
74
90
  } else {
75
- let parse = PARSERS[extname] || PARSERS.json;
91
+ let parse = opts?.parser ?? getParser(extname);
76
92
  config = parse(configContent);
77
93
  }
78
94
 
79
- return {
80
- config: config,
95
+ let output = {
96
+ config,
81
97
  files: [{filePath: configFile}],
82
98
  };
99
+
100
+ configCache.set(String(parse) + configFile, output);
101
+ return output;
83
102
  } catch (err) {
84
103
  if (err.code === 'MODULE_NOT_FOUND' || err.code === 'ENOENT') {
85
- existsCache.delete(configFile);
86
104
  return null;
87
105
  }
88
106
 
@@ -92,3 +110,18 @@ export async function loadConfig(
92
110
 
93
111
  return null;
94
112
  }
113
+
114
+ loadConfig.clear = () => {
115
+ configCache.reset();
116
+ resolveCache.clear();
117
+ };
118
+
119
+ function getParser(extname) {
120
+ switch (extname) {
121
+ case 'toml':
122
+ return toml;
123
+ case 'json':
124
+ default:
125
+ return json5;
126
+ }
127
+ }
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
  }
@@ -1,17 +1,22 @@
1
1
  // @flow
2
+
2
3
  export default function createDependencyLocation(
3
- start: {|
4
+ start: interface {
4
5
  line: number,
5
6
  column: number,
6
- |},
7
- moduleSpecifier: string,
7
+ },
8
+ specifier: string,
8
9
  lineOffset: number = 0,
9
10
  columnOffset: number = 0,
10
11
  // Imports are usually wrapped in quotes
11
12
  importWrapperLength: number = 2,
12
- ) {
13
+ ): {|
14
+ end: {|column: number, line: number|},
15
+ filePath: string,
16
+ start: {|column: number, line: number|},
17
+ |} {
13
18
  return {
14
- filePath: moduleSpecifier,
19
+ filePath: specifier,
15
20
  start: {
16
21
  line: start.line + lineOffset,
17
22
  column: start.column + columnOffset,
@@ -20,7 +25,7 @@ export default function createDependencyLocation(
20
25
  line: start.line + lineOffset,
21
26
  column:
22
27
  start.column +
23
- moduleSpecifier.length -
28
+ specifier.length -
24
29
  1 +
25
30
  importWrapperLength +
26
31
  columnOffset,
@@ -0,0 +1,158 @@
1
+ // @flow
2
+
3
+ import type {FilePath, PackagedBundle} from '@parcel/types';
4
+ import type {FileSystem} from '@parcel/fs';
5
+ import SourceMap from '@parcel/source-map';
6
+ import nullthrows from 'nullthrows';
7
+ import path from 'path';
8
+ import {loadSourceMapUrl} from './';
9
+
10
+ export type AssetStats = {|
11
+ filePath: string,
12
+ size: number,
13
+ originalSize: number,
14
+ time: number,
15
+ |};
16
+
17
+ export type BundleStats = {|
18
+ filePath: string,
19
+ size: number,
20
+ time: number,
21
+ assets: Array<AssetStats>,
22
+ |};
23
+
24
+ export type BuildMetrics = {|
25
+ bundles: Array<BundleStats>,
26
+ |};
27
+
28
+ async function getSourcemapSizes(
29
+ filePath: FilePath,
30
+ fs: FileSystem,
31
+ projectRoot: FilePath,
32
+ ): Promise<?Map<string, number>> {
33
+ let bundleContents = await fs.readFile(filePath, 'utf-8');
34
+ let mapUrlData = await loadSourceMapUrl(fs, filePath, bundleContents);
35
+ if (!mapUrlData) {
36
+ return null;
37
+ }
38
+
39
+ let rawMap = mapUrlData.map;
40
+ let sourceMap = new SourceMap(projectRoot);
41
+ sourceMap.addVLQMap(rawMap);
42
+ let parsedMapData = sourceMap.getMap();
43
+
44
+ if (parsedMapData.mappings.length > 2) {
45
+ let sources = parsedMapData.sources.map(s =>
46
+ path.normalize(path.join(projectRoot, s)),
47
+ );
48
+ let currLine = 1;
49
+ let currColumn = 0;
50
+ let currMappingIndex = 0;
51
+ let currMapping = parsedMapData.mappings[currMappingIndex];
52
+ let nextMapping = parsedMapData.mappings[currMappingIndex + 1];
53
+ let sourceSizes = new Array(sources.length).fill(0);
54
+ let unknownOrigin: number = 0;
55
+ for (let i = 0; i < bundleContents.length; i++) {
56
+ let character = bundleContents[i];
57
+
58
+ while (
59
+ nextMapping &&
60
+ nextMapping.generated.line === currLine &&
61
+ nextMapping.generated.column <= currColumn
62
+ ) {
63
+ currMappingIndex++;
64
+ currMapping = parsedMapData.mappings[currMappingIndex];
65
+ nextMapping = parsedMapData.mappings[currMappingIndex + 1];
66
+ }
67
+
68
+ let currentSource = currMapping.source;
69
+ let charSize = Buffer.byteLength(character, 'utf8');
70
+ if (
71
+ currentSource != null &&
72
+ currMapping.generated.line === currLine &&
73
+ currMapping.generated.column <= currColumn
74
+ ) {
75
+ sourceSizes[currentSource] += charSize;
76
+ } else {
77
+ unknownOrigin += charSize;
78
+ }
79
+
80
+ if (character === '\n') {
81
+ currColumn = 0;
82
+ currLine++;
83
+ } else {
84
+ currColumn++;
85
+ }
86
+ }
87
+
88
+ let sizeMap = new Map();
89
+ for (let i = 0; i < sourceSizes.length; i++) {
90
+ sizeMap.set(sources[i], sourceSizes[i]);
91
+ }
92
+
93
+ sizeMap.set('', unknownOrigin);
94
+
95
+ return sizeMap;
96
+ }
97
+ }
98
+
99
+ async function createBundleStats(
100
+ bundle: PackagedBundle,
101
+ fs: FileSystem,
102
+ projectRoot: FilePath,
103
+ ) {
104
+ let filePath = bundle.filePath;
105
+ let sourcemapSizes = await getSourcemapSizes(filePath, fs, projectRoot);
106
+
107
+ let assets: Map<string, AssetStats> = new Map();
108
+ bundle.traverseAssets(asset => {
109
+ let filePath = path.normalize(asset.filePath);
110
+ assets.set(filePath, {
111
+ filePath,
112
+ size: asset.stats.size,
113
+ originalSize: asset.stats.size,
114
+ time: asset.stats.time,
115
+ });
116
+ });
117
+
118
+ let assetsReport: Array<AssetStats> = [];
119
+ if (sourcemapSizes && sourcemapSizes.size) {
120
+ assetsReport = Array.from(sourcemapSizes.keys()).map((filePath: string) => {
121
+ let foundSize = sourcemapSizes.get(filePath) || 0;
122
+ let stats = assets.get(filePath) || {
123
+ filePath,
124
+ size: foundSize,
125
+ originalSize: foundSize,
126
+ time: 0,
127
+ };
128
+
129
+ return {
130
+ ...stats,
131
+ size: foundSize,
132
+ };
133
+ });
134
+ } else {
135
+ assetsReport = Array.from(assets.values());
136
+ }
137
+
138
+ return {
139
+ filePath: nullthrows(bundle.filePath),
140
+ size: bundle.stats.size,
141
+ time: bundle.stats.time,
142
+ assets: assetsReport.sort((a, b) => b.size - a.size),
143
+ };
144
+ }
145
+
146
+ export default async function generateBuildMetrics(
147
+ bundles: Array<PackagedBundle>,
148
+ fs: FileSystem,
149
+ projectRoot: FilePath,
150
+ ): Promise<BuildMetrics> {
151
+ bundles.sort((a, b) => b.stats.size - a.stats.size).filter(b => !!b.filePath);
152
+
153
+ return {
154
+ bundles: (
155
+ await Promise.all(bundles.map(b => createBundleStats(b, fs, projectRoot)))
156
+ ).filter(e => !!e),
157
+ };
158
+ }
@@ -8,7 +8,7 @@ export default async function generateCertificate(
8
8
  fs: FileSystem,
9
9
  cacheDir: string,
10
10
  host: ?string,
11
- ) {
11
+ ): Promise<{|cert: Buffer, key: Buffer|}> {
12
12
  let certDirectory = cacheDir;
13
13
 
14
14
  const privateKeyPath = path.join(certDirectory, 'private.pem');
@@ -5,7 +5,7 @@ import type {FileSystem} from '@parcel/fs';
5
5
  export default async function getCertificate(
6
6
  fs: FileSystem,
7
7
  options: HTTPSOptions,
8
- ) {
8
+ ): Promise<{|cert: Buffer, key: Buffer|}> {
9
9
  try {
10
10
  let cert = await fs.readFile(options.cert);
11
11
  let key = await fs.readFile(options.key);
@@ -14,10 +14,7 @@ export default function getExisting(
14
14
  return {
15
15
  source,
16
16
  minified: fs.existsSync(minifiedPath)
17
- ? fs
18
- .readFileSync(minifiedPath, 'utf8')
19
- .trim()
20
- .replace(/;$/, '')
17
+ ? fs.readFileSync(minifiedPath, 'utf8').trim().replace(/;$/, '')
21
18
  : source,
22
19
  };
23
20
  }
package/src/getRootDir.js CHANGED
@@ -2,8 +2,7 @@
2
2
 
3
3
  import type {FilePath} from '@parcel/types';
4
4
  import {isGlob} from './glob';
5
-
6
- const path = require('path');
5
+ import path from 'path';
7
6
 
8
7
  export default function getRootDir(files: Array<FilePath>): FilePath {
9
8
  let cur = null;
package/src/glob.js CHANGED
@@ -6,24 +6,41 @@ import type {FileSystem} from '@parcel/fs';
6
6
  import _isGlob from 'is-glob';
7
7
  import fastGlob, {type FastGlobOptions} from 'fast-glob';
8
8
  import {isMatch} from 'micromatch';
9
+ import {normalizeSeparators} from './path';
9
10
 
10
- function normalizePath(p: string): string {
11
- return p.replace(/\\/g, '/');
11
+ export function isGlob(p: FilePath): any {
12
+ return _isGlob(normalizeSeparators(p));
12
13
  }
13
14
 
14
- export function isGlob(p: FilePath) {
15
- return _isGlob(normalizePath(p));
16
- }
17
-
18
- export function isGlobMatch(filePath: FilePath, glob: Glob) {
19
- return isMatch(filePath, normalizePath(glob));
15
+ export function isGlobMatch(filePath: FilePath, glob: Glob): any {
16
+ return isMatch(filePath, normalizeSeparators(glob));
20
17
  }
21
18
 
22
19
  export function globSync(
23
20
  p: FilePath,
24
- options: FastGlobOptions<FilePath>,
21
+ fs: FileSystem,
22
+ options?: FastGlobOptions<FilePath>,
25
23
  ): Array<FilePath> {
26
- return fastGlob.sync(normalizePath(p), options);
24
+ // $FlowFixMe
25
+ options = {
26
+ ...options,
27
+ fs: {
28
+ statSync: p => {
29
+ return fs.statSync(p);
30
+ },
31
+ lstatSync: p => {
32
+ // Our FileSystem interface doesn't have lstat support at the moment,
33
+ // but this is fine for our purposes since we follow symlinks by default.
34
+ return fs.statSync(p);
35
+ },
36
+ readdirSync: (p, opts) => {
37
+ return fs.readdirSync(p, opts);
38
+ },
39
+ },
40
+ };
41
+
42
+ // $FlowFixMe
43
+ return fastGlob.sync(normalizeSeparators(p), options);
27
44
  }
28
45
 
29
46
  export function glob(
@@ -66,5 +83,6 @@ export function glob(
66
83
  },
67
84
  };
68
85
 
69
- return fastGlob(normalizePath(p), options);
86
+ // $FlowFixMe Added in Flow 0.121.0 upgrade in #4381
87
+ return fastGlob(normalizeSeparators(p), options);
70
88
  }