-
Notifications
You must be signed in to change notification settings - Fork 783
/
typescript-sys.ts
216 lines (186 loc) · 6.37 KB
/
typescript-sys.ts
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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
import type * as d from '../../../declarations';
import { basename, resolve } from 'path';
import { getCurrentDirectory, IS_CASE_SENSITIVE_FILE_NAMES, IS_WEB_WORKER_ENV, isRemoteUrl, isString, normalizePath, noop } from '@utils';
import { fetchUrlSync } from '../fetch/fetch-module-sync';
import { patchTypeScriptResolveModule } from './typescript-resolve-module';
import ts from 'typescript';
export const patchTsSystemFileSystem = (config: d.Config, stencilSys: d.CompilerSystem, inMemoryFs: d.InMemoryFileSystem, tsSys: ts.System) => {
const realpath = (path: string) => {
const rp = stencilSys.realpathSync(path);
if (isString(rp)) {
return rp;
}
return path;
};
const getAccessibleFileSystemEntries = (path: string) => {
try {
const entries = stencilSys.readDirSync(path || '.').sort();
const files: string[] = [];
const directories: string[] = [];
for (const absPath of entries) {
// This is necessary because on some file system node fails to exclude
// "." and "..". See https://github.com/nodejs/node/issues/4002
const stat = inMemoryFs.statSync(absPath);
if (!stat) {
continue;
}
const entry = basename(absPath);
if (stat.isFile) {
files.push(entry);
} else if (stat.isDirectory) {
directories.push(entry);
}
}
return { files, directories };
} catch (e) {
return { files: [], directories: [] };
}
};
tsSys.createDirectory = p => {
stencilSys.createDirSync(p, { recursive: true });
};
tsSys.directoryExists = p => {
const s = inMemoryFs.statSync(p);
return s.isDirectory;
};
tsSys.exit = stencilSys.exit;
tsSys.fileExists = p => {
let filePath = p;
if (isRemoteUrl(p)) {
filePath = getTypescriptPathFromUrl(config, tsSys.getExecutingFilePath(), p);
}
const s = inMemoryFs.statSync(filePath);
return !!(s && s.isFile);
};
tsSys.getCurrentDirectory = stencilSys.getCurrentDirectory;
tsSys.getExecutingFilePath = stencilSys.getCompilerExecutingPath;
tsSys.getDirectories = p => {
const items = stencilSys.readDirSync(p);
return items.filter(itemPath => {
const s = inMemoryFs.statSync(itemPath);
return !!(s && s.exists && s.isDirectory);
});
};
tsSys.readDirectory = (path, extensions, exclude, include, depth) => {
const cwd = stencilSys.getCurrentDirectory();
return (ts as any).matchFiles(path, extensions, exclude, include, IS_CASE_SENSITIVE_FILE_NAMES, cwd, depth, getAccessibleFileSystemEntries, realpath);
};
tsSys.readFile = p => {
let filePath = p;
const isUrl = isRemoteUrl(p);
if (isUrl) {
filePath = getTypescriptPathFromUrl(config, tsSys.getExecutingFilePath(), p);
}
let content = inMemoryFs.readFileSync(filePath, { useCache: isUrl });
if (typeof content !== 'string' && isUrl) {
if (IS_WEB_WORKER_ENV) {
content = fetchUrlSync(p);
if (typeof content === 'string') {
inMemoryFs.writeFile(filePath, content);
}
} else {
config.logger.error(`ts.sys can only request http resources from within a web worker: ${p}`);
}
}
return content;
};
tsSys.writeFile = (p, data) => inMemoryFs.writeFile(p, data);
return tsSys;
};
const patchTsSystemWatch = (stencilSys: d.CompilerSystem, tsSys: ts.System) => {
tsSys.watchDirectory = (p, cb, recursive) => {
const watcher = stencilSys.watchDirectory(
p,
filePath => {
cb(filePath);
},
recursive,
);
return {
close() {
watcher.close();
},
};
};
tsSys.watchFile = (p, cb) => {
const watcher = stencilSys.watchFile(p, (filePath, eventKind) => {
if (eventKind === 'fileAdd') {
cb(filePath, ts.FileWatcherEventKind.Created);
} else if (eventKind === 'fileUpdate') {
cb(filePath, ts.FileWatcherEventKind.Changed);
} else if (eventKind === 'fileDelete') {
cb(filePath, ts.FileWatcherEventKind.Deleted);
}
});
return {
close() {
watcher.close();
},
};
};
};
export const patchTypescript = (config: d.Config, inMemoryFs: d.InMemoryFileSystem) => {
if (!(ts as any).__patched) {
if (config.sys) {
patchTsSystemFileSystem(config, config.sys, inMemoryFs, ts.sys);
patchTsSystemWatch(config.sys, ts.sys);
}
patchTypeScriptResolveModule(config, inMemoryFs);
patchTypeScriptGetParsedCommandLineOfConfigFile();
(ts as any).__patched = true;
}
};
const patchTypeScriptSysMinimum = () => {
if (!ts.sys) {
// patches just the bare minimum
// if ts.sys already exists then it must be node ts.sys
// otherwise we're either browser or deno
// will be updated later on with the stencil sys
ts.sys = {
args: [],
createDirectory: noop,
directoryExists: () => false,
exit: noop,
fileExists: () => false,
getCurrentDirectory,
getDirectories: () => [],
getExecutingFilePath: () => './',
readDirectory: () => [],
readFile: noop,
newLine: '\n',
resolvePath: resolve,
useCaseSensitiveFileNames: false,
write: noop,
writeFile: noop,
};
}
};
patchTypeScriptSysMinimum();
export const getTypescriptPathFromUrl = (config: d.Config, tsExecutingUrl: string, url: string) => {
const tsBaseUrl = new URL('..', tsExecutingUrl).href;
if (url.startsWith(tsBaseUrl)) {
const tsFilePath = url.replace(tsBaseUrl, '/');
const tsNodePath = config.sys.getLocalModulePath({ rootDir: config.rootDir, moduleId: 'typescript', path: tsFilePath });
return normalizePath(tsNodePath);
}
return url;
};
export const patchTypeScriptGetParsedCommandLineOfConfigFile = () => {
const orgGetParsedCommandLineOfConfigFile = ts.getParsedCommandLineOfConfigFile;
ts.getParsedCommandLineOfConfigFile = (configFileName, optionsToExtend, host, extendedConfigCache) => {
const results = orgGetParsedCommandLineOfConfigFile(configFileName, optionsToExtend, host, extendedConfigCache);
// manually filter out any .spec or .e2e files
results.fileNames = results.fileNames.filter(f => {
// filter e2e tests
if (f.includes('.e2e.') || f.includes('/e2e.')) {
return false;
}
// filter spec tests
if (f.includes('.spec.') || f.includes('/spec.')) {
return false;
}
return true;
});
return results;
};
};