Skip to content

Commit d75cc6f

Browse files
author
Ives van Hoorne
committed
Support for TypeScript config
1 parent a9374fe commit d75cc6f

File tree

11 files changed

+183
-43
lines changed

11 files changed

+183
-43
lines changed

packages/app/src/app/components/CodeEditor/Monaco/index.js

Lines changed: 55 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ class MonacoEditor extends React.Component<Props, State> implements Editor {
5151
currentModule: $PropertyType<Props, 'currentModule'>;
5252
settings: $PropertyType<Props, 'settings'>;
5353
dependencies: ?$PropertyType<Props, 'dependencies'>;
54+
tsconfig: ?$PropertyType<Props, 'tsconfig'>;
5455
disposeInitializer: ?() => void;
5556
syntaxWorker: ?Worker;
5657
lintWorker: ?Worker;
@@ -69,6 +70,8 @@ class MonacoEditor extends React.Component<Props, State> implements Editor {
6970
this.settings = props.settings;
7071
this.dependencies = props.dependencies;
7172

73+
this.tsconfig = props.tsconfig;
74+
7275
this.syntaxWorker = null;
7376
this.lintWorker = null;
7477
this.typingsFetcherWorker = null;
@@ -130,48 +133,13 @@ class MonacoEditor extends React.Component<Props, State> implements Editor {
130133
});
131134
});
132135

133-
const hasNativeTypescript = this.hasNativeTypescript();
134-
135-
const compilerDefaults = {
136-
jsxFactory: 'React.createElement',
137-
reactNamespace: 'React',
138-
jsx: monaco.languages.typescript.JsxEmit.React,
139-
target: monaco.languages.typescript.ScriptTarget.ES2016,
140-
allowNonTsExtensions: !hasNativeTypescript,
141-
moduleResolution: monaco.languages.typescript.ModuleResolutionKind.NodeJs,
142-
module: hasNativeTypescript
143-
? monaco.languages.typescript.ModuleKind.ES2015
144-
: monaco.languages.typescript.ModuleKind.System,
145-
experimentalDecorators: true,
146-
noEmit: true,
147-
allowJs: true,
148-
typeRoots: ['node_modules/@types'],
149-
150-
forceConsistentCasingInFileNames: hasNativeTypescript,
151-
noImplicitReturns: hasNativeTypescript,
152-
noImplicitThis: hasNativeTypescript,
153-
noImplicitAny: hasNativeTypescript,
154-
strictNullChecks: hasNativeTypescript,
155-
suppressImplicitAnyIndexErrors: hasNativeTypescript,
156-
noUnusedLocals: hasNativeTypescript,
157-
};
158-
159136
monaco.languages.typescript.typescriptDefaults.setMaximunWorkerIdleTime(-1);
160137
monaco.languages.typescript.javascriptDefaults.setMaximunWorkerIdleTime(-1);
161-
monaco.languages.typescript.typescriptDefaults.setCompilerOptions(
162-
compilerDefaults
163-
);
164-
monaco.languages.typescript.javascriptDefaults.setCompilerOptions(
165-
compilerDefaults
166-
);
167138

168139
monaco.languages.typescript.typescriptDefaults.setEagerModelSync(true);
169140
monaco.languages.typescript.javascriptDefaults.setEagerModelSync(true);
170141

171-
monaco.languages.typescript.typescriptDefaults.setDiagnosticsOptions({
172-
noSemanticValidation: false,
173-
noSyntaxValidation: !hasNativeTypescript,
174-
});
142+
this.setCompilerOptions();
175143

176144
const sandbox = this.sandbox;
177145
const currentModule = this.currentModule;
@@ -236,6 +204,57 @@ class MonacoEditor extends React.Component<Props, State> implements Editor {
236204
}
237205
};
238206

207+
setCompilerOptions = () => {
208+
const hasNativeTypescript = this.hasNativeTypescript();
209+
const existingConfig = this.tsconfig ? this.tsconfig.compilerOptions : {};
210+
211+
const compilerDefaults = {
212+
jsxFactory: 'React.createElement',
213+
reactNamespace: 'React',
214+
jsx: this.monaco.languages.typescript.JsxEmit.React,
215+
target: this.monaco.languages.typescript.ScriptTarget.ES2016,
216+
allowNonTsExtensions: !hasNativeTypescript,
217+
moduleResolution: this.monaco.languages.typescript.ModuleResolutionKind
218+
.NodeJs,
219+
module: hasNativeTypescript
220+
? this.monaco.languages.typescript.ModuleKind.ES2015
221+
: this.monaco.languages.typescript.ModuleKind.System,
222+
experimentalDecorators: true,
223+
noEmit: true,
224+
allowJs: true,
225+
typeRoots: ['node_modules/@types'],
226+
227+
forceConsistentCasingInFileNames:
228+
hasNativeTypescript && existingConfig.forceConsistentCasingInFileNames,
229+
noImplicitReturns:
230+
hasNativeTypescript && existingConfig.noImplicitReturns,
231+
noImplicitThis: hasNativeTypescript && existingConfig.noImplicitThis,
232+
noImplicitAny: hasNativeTypescript && existingConfig.noImplicitAny,
233+
strictNullChecks: hasNativeTypescript && existingConfig.strictNullChecks,
234+
suppressImplicitAnyIndexErrors:
235+
hasNativeTypescript && existingConfig.suppressImplicitAnyIndexErrors,
236+
noUnusedLocals: hasNativeTypescript && existingConfig.noUnusedLocals,
237+
};
238+
239+
this.monaco.languages.typescript.typescriptDefaults.setCompilerOptions(
240+
compilerDefaults
241+
);
242+
this.monaco.languages.typescript.javascriptDefaults.setCompilerOptions(
243+
compilerDefaults
244+
);
245+
246+
this.monaco.languages.typescript.typescriptDefaults.setDiagnosticsOptions({
247+
noSemanticValidation: false,
248+
noSyntaxValidation: !hasNativeTypescript,
249+
});
250+
};
251+
252+
setTSConfig = (config: Object) => {
253+
this.tsconfig = config;
254+
255+
this.setCompilerOptions();
256+
};
257+
239258
changeModule = (
240259
newModule: Module,
241260
errors?: Array<ModuleError>,

packages/app/src/app/components/CodeEditor/index.js

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import type { Sandbox } from 'common/types';
1313
import { getModulePath } from 'common/sandbox/modules';
1414
import Tooltip from 'common/components/Tooltip';
1515
import UIIcon from 'react-icons/lib/md/dvr';
16+
import QuestionIcon from 'react-icons/lib/go/question';
1617

1718
import type { Props } from './types';
1819
import Monaco from './Monaco';
@@ -149,7 +150,21 @@ export default class CodeEditor extends React.PureComponent<Props, State> {
149150
</Icons>
150151
) : (
151152
<Icons style={{ fontSize: '.875rem' }}>
152-
Supported Configuration
153+
{config.partialSupportDisclaimer ? (
154+
<Tooltip
155+
position="bottom"
156+
title={config.partialSupportDisclaimer}
157+
style={{
158+
display: 'flex',
159+
alignItems: 'center',
160+
}}
161+
>
162+
Partially Supported Config{' '}
163+
<QuestionIcon style={{ marginLeft: '.5rem' }} />
164+
</Tooltip>
165+
) : (
166+
<div>Supported Configuration</div>
167+
)}
153168
</Icons>
154169
))}
155170
<Editor {...props} dependencies={dependencies} />

packages/app/src/app/components/CodeEditor/types.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ export interface Editor {
3838
changeCode?: (code: string) => any;
3939

4040
currentModule?: Module;
41+
setTSConfig?: (tsConfig: Object) => void;
4142
}
4243

4344
export type Props = {
@@ -54,4 +55,5 @@ export type Props = {
5455
hideNavigation?: boolean,
5556
dependencies?: ?{ [name: string]: string },
5657
highlightedLines?: Array<number>,
58+
tsconfig?: Object,
5759
};

packages/app/src/app/pages/Sandbox/Editor/Content/index.js

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ class EditorPreview extends React.Component<Props, State> {
171171
}
172172
);
173173
const disposePackageHandler = reaction(
174-
() => store.editor.parsedConfigurations,
174+
() => store.editor.parsedConfigurations.package,
175175
() => {
176176
const { parsed } = store.editor.parsedConfigurations.package;
177177
if (parsed) {
@@ -183,6 +183,19 @@ class EditorPreview extends React.Component<Props, State> {
183183
}
184184
}
185185
);
186+
const disposeTSConfigHandler = reaction(
187+
() => store.editor.parsedConfigurations.typescript,
188+
() => {
189+
if (store.editor.parsedConfigurations.typescript) {
190+
const { parsed } = store.editor.parsedConfigurations.typescript;
191+
if (parsed) {
192+
if (editor.setTSConfig) {
193+
editor.setTSConfig(parsed);
194+
}
195+
}
196+
}
197+
}
198+
);
186199
const disposeModuleHandler = reaction(
187200
() => [store.editor.currentModule, store.editor.currentModule.code],
188201
([newModule]) => {
@@ -214,6 +227,7 @@ class EditorPreview extends React.Component<Props, State> {
214227
disposeModulesHandler();
215228
disposePreferencesHandler();
216229
disposePackageHandler();
230+
disposeTSConfigHandler();
217231
disposeSandboxChangeHandler();
218232
disposeModuleHandler();
219233
disposeToggleDevtools();
@@ -330,6 +344,10 @@ class EditorPreview extends React.Component<Props, State> {
330344
moduleShortid: currentModule.shortid,
331345
})
332346
}
347+
tsconfig={
348+
store.editor.parsedConfigurations.typescript &&
349+
store.editor.parsedConfigurations.typescript.parsed
350+
}
333351
/>
334352
<Preview width={this.state.width} height={this.state.height} />
335353
</div>

packages/app/src/sandbox/eval/transpilers/typescript/index.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,20 @@ class TypeScriptTranspiler extends WorkerTranspiler {
1515
return new Promise((resolve, reject) => {
1616
const path = loaderContext.path;
1717

18+
let foundConfig = loaderContext.options;
19+
if (
20+
loaderContext.options.configurations &&
21+
loaderContext.options.configurations.typescript &&
22+
loaderContext.options.configurations.typescript.parsed
23+
) {
24+
foundConfig = loaderContext.options.configurations.typescript.parsed;
25+
}
26+
1827
this.queueTask(
1928
{
2029
code,
2130
path,
31+
config: foundConfig,
2232
},
2333
loaderContext,
2434
(err, data) => {

packages/app/src/sandbox/eval/transpilers/typescript/typescript-worker.js

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { buildWorkerError } from '../utils/worker-error-handler';
22
import getDependencies from './get-require-statements';
33

44
self.importScripts([
5-
'https://cdnjs.cloudflare.com/ajax/libs/typescript/2.5.0/typescript.min.js',
5+
'https://cdnjs.cloudflare.com/ajax/libs/typescript/2.7.2/typescript.min.js',
66
]);
77

88
self.postMessage('ready');
@@ -20,9 +20,9 @@ declare var ts: {
2020
};
2121

2222
self.addEventListener('message', event => {
23-
const { code, path } = event.data;
23+
const { code, path, config } = event.data;
2424

25-
const config = {
25+
const defaultConfig = {
2626
fileName: path,
2727
reportDiagnostics: true,
2828
compilerOptions: {
@@ -49,8 +49,19 @@ self.addEventListener('message', event => {
4949
},
5050
};
5151

52+
let finalConfig = defaultConfig;
53+
54+
if (config) {
55+
finalConfig = { ...config };
56+
finalConfig.compilerOptions = {
57+
...config.compilerOptions,
58+
module: ts.ModuleKind.CommonJS,
59+
moduleResolution: ts.ModuleResolutionKind.NodeJs,
60+
};
61+
}
62+
5263
try {
53-
const { outputText: compiledCode } = ts.transpileModule(code, config);
64+
const { outputText: compiledCode } = ts.transpileModule(code, finalConfig);
5465

5566
const sourceFile = ts.createSourceFile(
5667
path,

packages/common/templates/configuration/eslintrc/index.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ const config: ConfigurationFile = {
1212
},
1313

1414
schema:
15-
'https://raw.githubusercontent.com/SchemaStore/schemastore/master/src/schemas/json/babelrc.json',
15+
'https://raw.githubusercontent.com/SchemaStore/schemastore/master/src/schemas/json/eslintrc.json',
16+
17+
partialSupportDisclaimer: `We can only read preinstalled rules (all from eslint, react, prettier, import and vue plugins).`,
1618
};
1719

1820
export default config;

packages/common/templates/configuration/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,15 @@ import prettierRC from './prettierRC';
44
import sandboxConfig from './sandbox';
55
import babelrc from './babelrc';
66
import angularCli from './angular-cli';
7+
import tsconfig from './tsconfig';
78

89
const configs = {
910
babelrc,
1011
packageJSON,
1112
prettierRC,
1213
sandboxConfig,
1314
angularCli,
15+
tsconfig,
1416
};
1517

1618
export default configs;
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
// @flow
2+
import type { ConfigurationFile } from '../types';
3+
4+
const config: ConfigurationFile = {
5+
title: 'tsconfig.json',
6+
type: 'typescript',
7+
description: 'Configuration for how TypeScript transpiles.',
8+
moreInfoUrl: 'http://www.typescriptlang.org/docs/handbook/tsconfig-json.html',
9+
10+
getDefaultCode: (template: string) => {
11+
if (template === 'create-react-app-typescript') {
12+
return JSON.stringify(
13+
{
14+
compilerOptions: {
15+
outDir: 'build/dist',
16+
module: 'esnext',
17+
target: 'es5',
18+
lib: ['es6', 'dom'],
19+
sourceMap: true,
20+
allowJs: true,
21+
jsx: 'react',
22+
moduleResolution: 'node',
23+
rootDir: 'src',
24+
forceConsistentCasingInFileNames: true,
25+
noImplicitReturns: true,
26+
noImplicitThis: true,
27+
noImplicitAny: true,
28+
strictNullChecks: true,
29+
suppressImplicitAnyIndexErrors: true,
30+
noUnusedLocals: true,
31+
},
32+
exclude: [
33+
'node_modules',
34+
'build',
35+
'scripts',
36+
'acceptance-tests',
37+
'webpack',
38+
'jest',
39+
'src/setupTests.ts',
40+
],
41+
},
42+
null,
43+
2
44+
);
45+
}
46+
47+
return '{}';
48+
},
49+
50+
schema:
51+
'https://raw.githubusercontent.com/SchemaStore/schemastore/master/src/schemas/json/tsconfig.json',
52+
53+
partialSupportDisclaimer: `Only \`compilerOptions\` field is supported.`,
54+
};
55+
56+
export default config;

packages/common/templates/configuration/types.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,5 @@ export type ConfigurationFile = {
2020
},
2121

2222
schema?: string,
23+
partialSupportDisclaimer?: string,
2324
};

0 commit comments

Comments
 (0)