1 |
|
2 |
|
3 | import type {ConfigResult, File, FilePath} from '@parcel/types';
|
4 | import type {FileSystem} from '@parcel/fs';
|
5 | import path from 'path';
|
6 | import clone from 'clone';
|
7 |
|
8 | export type ConfigOutput = {|
|
9 | config: ConfigResult,
|
10 | files: Array<File>,
|
11 | |};
|
12 |
|
13 | export type ConfigOptions = {|
|
14 | parse?: boolean,
|
15 | |};
|
16 |
|
17 | const PARSERS = {
|
18 | json: require('json5').parse,
|
19 | toml: require('@iarna/toml').parse,
|
20 | };
|
21 |
|
22 | export async function resolveConfig(
|
23 | fs: FileSystem,
|
24 | filepath: FilePath,
|
25 | filenames: Array<FilePath>,
|
26 | opts: ?ConfigOptions,
|
27 | root: FilePath = path.parse(filepath).root,
|
28 | ): Promise<FilePath | null> {
|
29 | filepath = await fs.realpath(path.dirname(filepath));
|
30 |
|
31 |
|
32 | if (path.basename(filepath) === 'node_modules') {
|
33 | return null;
|
34 | }
|
35 |
|
36 | for (const filename of filenames) {
|
37 | let file = path.join(filepath, filename);
|
38 | if ((await fs.exists(file)) && (await fs.stat(file)).isFile()) {
|
39 | return file;
|
40 | }
|
41 | }
|
42 |
|
43 | if (filepath === root) {
|
44 | return null;
|
45 | }
|
46 |
|
47 | return resolveConfig(fs, filepath, filenames, opts);
|
48 | }
|
49 |
|
50 | export function resolveConfigSync(
|
51 | fs: FileSystem,
|
52 | filepath: FilePath,
|
53 | filenames: Array<FilePath>,
|
54 | opts: ?ConfigOptions,
|
55 | root: FilePath = path.parse(filepath).root,
|
56 | ): FilePath | null {
|
57 | filepath = fs.realpathSync(path.dirname(filepath));
|
58 |
|
59 |
|
60 | if (filepath === root || path.basename(filepath) === 'node_modules') {
|
61 | return null;
|
62 | }
|
63 |
|
64 | for (const filename of filenames) {
|
65 | let file = path.join(filepath, filename);
|
66 | if (fs.existsSync(file) && fs.statSync(file).isFile()) {
|
67 | return file;
|
68 | }
|
69 | }
|
70 |
|
71 | return resolveConfigSync(fs, filepath, filenames, opts);
|
72 | }
|
73 |
|
74 | export async function loadConfig(
|
75 | fs: FileSystem,
|
76 | filepath: FilePath,
|
77 | filenames: Array<FilePath>,
|
78 | opts: ?ConfigOptions,
|
79 | ): Promise<ConfigOutput | null> {
|
80 | let configFile = await resolveConfig(fs, filepath, filenames, opts);
|
81 | if (configFile) {
|
82 | try {
|
83 | let extname = path.extname(configFile).slice(1);
|
84 | if (extname === 'js') {
|
85 | return {
|
86 |
|
87 | config: clone(require(configFile)),
|
88 | files: [{filePath: configFile}],
|
89 | };
|
90 | }
|
91 |
|
92 | let configContent = await fs.readFile(configFile, 'utf8');
|
93 | if (!configContent) {
|
94 | return null;
|
95 | }
|
96 |
|
97 | let config;
|
98 | if (opts && opts.parse === false) {
|
99 | config = configContent;
|
100 | } else {
|
101 | let parse = PARSERS[extname] || PARSERS.json;
|
102 | config = parse(configContent);
|
103 | }
|
104 |
|
105 | return {
|
106 | config: config,
|
107 | files: [{filePath: configFile}],
|
108 | };
|
109 | } catch (err) {
|
110 | if (err.code === 'MODULE_NOT_FOUND' || err.code === 'ENOENT') {
|
111 | return null;
|
112 | }
|
113 |
|
114 | throw err;
|
115 | }
|
116 | }
|
117 |
|
118 | return null;
|
119 | }
|