Skip to content

Commit c195aa1

Browse files
jamesbirtleskaisermann
authored andcommitted
feat: 🎸 add typescript preprocessor
1 parent 8334fed commit c195aa1

15 files changed

+200
-18
lines changed

Diff for: ‎README.md

+27-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Svelte Preprocess
22

3-
> A [Svelte](https://svelte.technology) preprocessor wrapper with support for: PostCSS, SCSS, Less, Stylus, Coffeescript and Pug.
3+
> A [Svelte](https://svelte.technology) preprocessor wrapper with support for: PostCSS, SCSS, Less, Stylus, Coffeescript, TypeScript and Pug.
44
55
## Installation
66

@@ -10,6 +10,7 @@ The preprocessor module installation is up to the developer.
1010

1111
- `postcss`: `npm install --save-dev postcss`
1212
- `coffeescript`: `npm install --save-dev coffeescript`
13+
- `typescript`: `npm install --save-dev typescript`
1314
- `less`: `npm install --save-dev less`
1415
- `sass`: `npm install --save-dev node-sass`
1516
- `pug`: `npm install --save-dev pug`
@@ -43,7 +44,7 @@ _Note: only for auto preprocessing_
4344

4445
### Preprocessors support
4546

46-
Current supported out-of-the-box preprocessors are `SCSS`, `Stylus`, `Less`, `Coffeescript`, `Pug` and `PostCSS`.
47+
Current supported out-of-the-box preprocessors are `SCSS`, `Stylus`, `Less`, `Coffeescript`, `TypeScript`, `Pug` and `PostCSS`.
4748

4849
```html
4950
<template lang="pug">
@@ -57,6 +58,10 @@ Current supported out-of-the-box preprocessors are `SCSS`, `Stylus`, `Less`, `Co
5758
console.log('Hey')
5859
</script>
5960

61+
<script lang="typescript">
62+
export const hello: string = 'world';
63+
</script>
64+
6065
<style src="./style.scss"></style>
6166

6267
<!-- Or -->
@@ -128,6 +133,26 @@ const options = {
128133
]
129134
},
130135
136+
typescript: {
137+
/**
138+
* Optionally specify the directory to load the tsconfig from
139+
*/
140+
tsconfigDirectory: './configs',
141+
142+
/**
143+
* Optionally specify the full path to the tsconfig
144+
*/
145+
tsconfigFile: './tsconfig.app.json',
146+
147+
/**
148+
* Optionally specify compiler options.
149+
* These will be merged with options from the tsconfig if found.
150+
*/
151+
compilerOptions: {
152+
module: 'es2015'
153+
}
154+
},
155+
131156
/** Use a custom preprocess method by passing a function. */
132157
pug({ content, filename }) {
133158
const code = pug.render(content)

Diff for: ‎package-lock.json

+7-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Diff for: ‎package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,8 @@
5151
"prettier": "^1.16.4",
5252
"pug": "^2.0.3",
5353
"stylus": "^0.54.5",
54-
"svelte": "^3.0.0-beta.16"
54+
"svelte": "^3.0.0-beta.16",
55+
"typescript": "^3.4.5"
5556
},
5657
"peerDependencies": {
5758
"svelte": "^1.44.0 || ^2.0.0 || ^3.0.0"

Diff for: ‎src/index.d.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ type PreprocessResult = {
1010
}
1111

1212
type SveltePreprocessOptions = {
13-
onBefore?({ content, filename } = { content: string, filename: string }): string;
13+
onBefore?({ content, filename }: { content: string, filename: string }): string;
1414
transformers?: {
1515
[languageName: string]: boolean | object | (({ content, filename }: { content: string, filename?: string }) =>
1616
PreprocessResult | Promise<PreprocessResult>)

Diff for: ‎src/index.js

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ module.exports.pug = opts => require(`./processors/pug.js`)(opts)
1111
module.exports.coffeescript = opts =>
1212
require(`./processors/coffeescript.js`)(opts)
1313
module.exports.coffee = opts => require(`./processors/coffeescript.js`)(opts)
14+
module.exports.typescript = opts => require(`./processors/typescript.js`)(opts)
1415

1516
/** Style */
1617
module.exports.less = opts => require(`./processors/less.js`)(opts)

Diff for: ‎src/processors/typescript.js

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
const { getLanguage } = require('../utils.js')
2+
const transformer = require('../transformers/typescript.js')
3+
4+
module.exports = options => ({
5+
script({ content, attributes, filename }) {
6+
const { lang } = getLanguage(attributes, 'javascript')
7+
if (lang !== 'typescript') return { code: content }
8+
9+
return transformer({ content, filename, options })
10+
},
11+
})

Diff for: ‎src/transformers/typescript.js

+65
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
const ts = require('typescript')
2+
const path = require('path')
3+
4+
module.exports = ({ content, filename, options }) => {
5+
const fileDirectory = options.tsconfigDirectory || path.dirname(filename)
6+
const tsconfigPath =
7+
options.tsconfigPath || ts.findConfigFile(fileDirectory, ts.sys.fileExists)
8+
const basePath = tsconfigPath ? path.dirname(tsconfigPath) : process.cwd()
9+
10+
let compilerOptionsJSON = options.compilerOptions || {}
11+
if (tsconfigPath) {
12+
const { error, config } = ts.readConfigFile(tsconfigPath, ts.sys.readFile)
13+
if (error) {
14+
const err = ts.formatDiagnostic(
15+
error,
16+
createFormatDiagnosticsHost(basePath),
17+
)
18+
throw new Error(err)
19+
}
20+
compilerOptionsJSON = {
21+
...(config.compilerOptions || {}),
22+
...compilerOptionsJSON,
23+
}
24+
}
25+
26+
const {
27+
errors,
28+
options: compilerOptions,
29+
} = ts.convertCompilerOptionsFromJson(compilerOptionsJSON, basePath)
30+
if (errors.length) {
31+
const err = ts.formatDiagnostics(
32+
errors,
33+
createFormatDiagnosticsHost(basePath),
34+
)
35+
throw new Error(err)
36+
}
37+
38+
const {
39+
outputText: code,
40+
sourceMapText: map,
41+
diagnostics,
42+
} = ts.transpileModule(content, {
43+
fileName: filename,
44+
compilerOptions: compilerOptions,
45+
reportDiagnostics: options.reportDiagnostics !== false,
46+
})
47+
48+
if (diagnostics.length) {
49+
const diagnosticMessage = ts.formatDiagnostics(
50+
diagnostics,
51+
createFormatDiagnosticsHost(basePath),
52+
)
53+
console.log(diagnosticMessage)
54+
}
55+
56+
return { code, map }
57+
}
58+
59+
function createFormatDiagnosticsHost(cwd) {
60+
return {
61+
getCanonicalFileName: fileName => fileName,
62+
getCurrentDirectory: () => cwd,
63+
getNewLine: () => ts.sys.newLine,
64+
}
65+
}

Diff for: ‎src/utils.js

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ const LANG_DICT = new Map([
77
['styl', 'stylus'],
88
['js', 'javascript'],
99
['coffee', 'coffeescript'],
10+
['ts', 'typescript'],
1011
])
1112

1213
const throwError = msg => {

Diff for: ‎test/autoProcess/script.test.js

+67-2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@ const {
55
doesCompileThrow,
66
} = require('../utils.js')
77

8-
const SCRIPT_LANGS = [['coffeescript', 'coffee']]
8+
const SCRIPT_LANGS = [
9+
['coffeescript', 'coffee'],
10+
['typescript', 'ts', { compilerOptions: { module: 'es2015' } }],
11+
]
912
const EXPECTED_SCRIPT = getFixtureContent('script.js')
1013

1114
SCRIPT_LANGS.forEach(([lang, ext, langOptions]) => {
@@ -28,9 +31,71 @@ SCRIPT_LANGS.forEach(([lang, ext, langOptions]) => {
2831
})
2932

3033
it(`should parse ${lang}`, async () => {
31-
const opts = getAutoPreprocess()
34+
const opts = getAutoPreprocess({
35+
transformers: {
36+
[lang]: langOptions,
37+
},
38+
})
3239
const preprocessed = await preprocess(template, opts)
3340
expect(preprocessed.toString()).toContain(EXPECTED_SCRIPT)
3441
})
3542
})
3643
})
44+
45+
describe('script - preprocessor - typescript', () => {
46+
const template = `<div></div><script lang="ts">${getFixtureContent(
47+
'script.ts',
48+
)}</script>`
49+
50+
it('should work with no compilerOptions', async () => {
51+
const opts = getAutoPreprocess()
52+
const preprocessed = await preprocess(template, opts)
53+
expect(preprocessed.toString()).toContain('exports.hello')
54+
})
55+
56+
it('should work with tsconfigDirectory', async () => {
57+
const opts = getAutoPreprocess({
58+
transformers: {
59+
typescript: {
60+
tsconfigDirectory: './test/fixtures',
61+
},
62+
},
63+
})
64+
const preprocessed = await preprocess(template, opts)
65+
expect(preprocessed.toString()).toContain(EXPECTED_SCRIPT)
66+
})
67+
68+
it('should work with tsconfigPath', async () => {
69+
const opts = getAutoPreprocess({
70+
transformers: {
71+
typescript: {
72+
tsconfigPath: './test/fixtures/tsconfig.json',
73+
},
74+
},
75+
})
76+
const preprocessed = await preprocess(template, opts)
77+
expect(preprocessed.toString()).toContain(EXPECTED_SCRIPT)
78+
})
79+
80+
it('should report syntactic errors in tsconfig file', () => {
81+
const opts = getAutoPreprocess({
82+
transformers: {
83+
typescript: {
84+
tsconfigPath: './test/fixtures/tsconfig.syntactic.json',
85+
},
86+
},
87+
})
88+
return expect(preprocess(template, opts)).rejects.toThrow('TS1005')
89+
})
90+
91+
it('should report semantic errors in tsconfig file', () => {
92+
const opts = getAutoPreprocess({
93+
transformers: {
94+
typescript: {
95+
tsconfigPath: './test/fixtures/tsconfig.semantic.json',
96+
},
97+
},
98+
})
99+
return expect(preprocess(template, opts)).rejects.toThrow('TS6046')
100+
})
101+
})

Diff for: ‎test/fixtures/script.coffee

+1-4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1 @@
1-
export default
2-
methods:
3-
foo: () ->
4-
console.log('Hey')
1+
export hello = 'world';

Diff for: ‎test/fixtures/script.js

+1-7
Original file line numberDiff line numberDiff line change
@@ -1,7 +1 @@
1-
export default {
2-
methods: {
3-
foo: function() {
4-
return console.log('Hey');
5-
}
6-
}
7-
};
1+
export var hello = 'world';

Diff for: ‎test/fixtures/script.ts

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export let hello: string = 'world';

Diff for: ‎test/fixtures/tsconfig.json

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"compilerOptions": {
3+
"module": "es2015"
4+
}
5+
}

Diff for: ‎test/fixtures/tsconfig.semantic.json

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"compilerOptions": {
3+
"module": "es2010"
4+
}
5+
}

Diff for: ‎test/fixtures/tsconfig.syntactic.json

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"compilerOptions": {
3+
"module" "es2015"
4+
}
5+
}

0 commit comments

Comments
 (0)