1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
|
#!/usr/bin/env node
/**
* @file This file generates a
* [jsconfig.json](https://code.visualstudio.com/docs/languages/jsconfig) file
* using aliases from the webpack config. To use it run from project root:
*
* ```sh
* node ./scripts/frontend/create_jsconfig.js
* ```
*
* NOTE: since aliases are currently generated based on solely Webpack config,
* aliases defined in Jest config might be missing.
*/
const fs = require('node:fs/promises');
const path = require('node:path');
const readline = require('node:readline/promises');
const { stdin, stdout } = require('node:process');
const chalk = require('chalk').default;
const prettier = require('prettier');
const PATH_PROJECT_ROOT = path.resolve(__dirname, '..', '..');
const PATH_JS_CONFIG = path.join(PATH_PROJECT_ROOT, 'jsconfig.json');
/**
* Creates and writes a jsconfig.json file, based on Webpack aliases.
*/
async function createJsConfig() {
// eslint-disable-next-line global-require
const webpackConfig = require('../../config/webpack.config');
// Aliases
const paths = {
// NOTE: Sentry is exposed via a wrapper, which has a limited API.
'@sentry/browser': [
path.relative(PATH_PROJECT_ROOT, 'app/assets/javascripts/sentry/sentry_browser_wrapper.js'),
],
};
const WEBPACK_ALIAS_EXCEPTIONS = [
'jquery$',
'@gitlab/svgs/dist/icons.svg',
'@apollo/client$',
'@sentry/browser$',
];
Object.entries(webpackConfig.resolve.alias)
.filter(([key]) => !WEBPACK_ALIAS_EXCEPTIONS.includes(key))
.forEach(([key, value]) => {
const alias = `${key}/*`;
const target = [`${path.relative(PATH_PROJECT_ROOT, value)}/*`];
paths[alias] = target;
});
// JS/TS config. See more: https://www.typescriptlang.org/tsconfig
const jsConfig = {
// As we're introducing jsconfig to the project, as a precaution we add both:
// 'include' and 'exclude' options. This might be simplified in the future.
exclude: ['node_modules', 'vendor'],
// 'include' is currently manually defined. We might want to append manually
// defined paths with paths from aliases
include: [
'app/assets/javascripts',
'ee/app/assets/javascripts',
'spec/frontend',
'ee/spec/frontend',
'tmp/tests/frontend/fixtures',
'tmp/tests/frontend/fixtures-ee',
],
// Explicitly enable automatic type acquisition
// See more: https://www.typescriptlang.org/tsconfig#type-acquisition
typeAcquisition: {
enable: true,
},
compilerOptions: {
baseUrl: '.', // Define the project root
checkJs: false, // Disable type checking on JavaScript files
disableSizeLimit: true, // Disable memory size limit for the language server
skipLibCheck: true, // Skip type checking all .d.ts files
resolveJsonModule: true, // Enable importing .json files
paths, // Aliases
},
};
// Stringify, format and update the config file
const jsConfigString = prettier.format(JSON.stringify(jsConfig, null, 2), { parser: 'json' });
await fs.writeFile(PATH_JS_CONFIG, jsConfigString);
}
function fileExists(filePath) {
return fs.stat(filePath).then(
() => true,
() => false,
);
}
async function main() {
const jsconfigExists = await fileExists(PATH_JS_CONFIG);
if (jsconfigExists) {
console.log(`${chalk.yellow('WARNING:')} jsconfig.json file already exists.`);
console.log('');
const rl = readline.createInterface({ input: stdin, output: stdout });
const response = await rl.question('Would you like to overwrite it? (y/n) ');
rl.close();
console.log('');
const shouldOverwrite = response.match(/^y(es)?$/i);
if (!shouldOverwrite) {
console.log('Overwrite cancelled.');
return;
}
}
try {
await createJsConfig();
console.log(chalk.green('jsconfig.json file created.'));
} catch (error) {
console.log(`${chalk.red('ERROR:')} failed to create jsconfig.json. See the error below:`);
console.error(error);
}
}
main();
|