Skip to content

Commit b94377f

Browse files
committed
Initial commit 🐣
0 parents  commit b94377f

24 files changed

+5028
-0
lines changed

.editorconfig

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
root = true
2+
3+
[*]
4+
indent_style = space
5+
indent_size = 2
6+
end_of_line = lf
7+
charset = utf-8
8+
trim_trailing_whitespace = true
9+
insert_final_newline = false

.eslintignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/test/fixtures

.eslintrc

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"root": true,
3+
"extends": ["standard"],
4+
"env": {
5+
"browser": true,
6+
"jest": true
7+
},
8+
"parserOptions": {
9+
"ecmaFeatures": {
10+
"modules": true
11+
}
12+
}
13+
}

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
node_modules
2+
*.log

.prettierrc

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"semi": false,
3+
"printWidth": 80,
4+
"trailingComma": "all",
5+
"bracketSpacing": true,
6+
"jsxBracketSameLine": false,
7+
"singleQuote": true
8+
}

README.md

+86
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
# Svelte Smart Preprocess (WIP)
2+
3+
> A smart svelte preprocessor wrapper
4+
5+
## Usage
6+
7+
```js
8+
const preprocessOptions = smartPreprocess({
9+
[language]: false | true | fn(content,filename): { code, map }
10+
})
11+
12+
svelte.preprocess(input, preprocessOptions).then(...)
13+
```
14+
15+
## Features
16+
17+
### Template (a la Vue) tag support
18+
19+
```html
20+
<template>
21+
<div>Hey</div>
22+
</template>
23+
24+
<style></style>
25+
26+
<script></script>
27+
```
28+
29+
## External files support
30+
31+
```html
32+
<template src="template.html"></template>
33+
<script src="./script.js"></script>
34+
<style src="./style.css"></style>
35+
```
36+
37+
## Preprocessors support
38+
39+
Current supported out-of-the-box preprocessors are `SCSS`, `Stylus`, `Less`, `Coffeescript`, `Pug`.
40+
41+
```html
42+
<div>Hey</div>
43+
44+
<style src="./style.scss"></style>
45+
46+
<style src="./style.styl"></style>
47+
48+
<style lang="scss">
49+
$color: red;
50+
div {
51+
color: $color;
52+
}
53+
</style>
54+
55+
<style type="text/stylus">
56+
$color = red
57+
58+
div
59+
color: $color
60+
</style>
61+
```
62+
63+
## Example
64+
65+
```js
66+
const svelte = require('svelte')
67+
const smartPreprocess = require('svelte-smart-preprocess')
68+
69+
const input = '...'
70+
71+
const preprocessOptions = smartPreprocess({
72+
/** Use included scss compiler */
73+
scss: true
74+
/**
75+
* Pass a function which returns an object { code, map }
76+
* or a promise that resolves to one.
77+
*/
78+
pug: function (content, filename, options) {
79+
const code = pug.render(content)
80+
81+
return { code, map: null }
82+
}
83+
})
84+
85+
svelte.preprocess(input, preprocessOptions).then(...)
86+
```

package.json

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
{
2+
"name": "svelte-smart-preprocess",
3+
"version": "0.1.0",
4+
"description": "Smart svelte preprocess wrapper",
5+
"main": "src/index.js",
6+
"repository": "https://github.com/kaisermann/svelte-smart-preprocess",
7+
"author": "Christian Kaisermann <[email protected]>",
8+
"license": "MIT",
9+
"scripts": {
10+
"test": "jest --no-cache --verbose"
11+
},
12+
"devDependencies": {
13+
"eslint": "^4.19.1",
14+
"eslint-config-standard": "^11.0.0",
15+
"eslint-plugin-import": "^2.11.0",
16+
"eslint-plugin-node": "^6.0.1",
17+
"eslint-plugin-prettier": "^2.6.0",
18+
"eslint-plugin-promise": "^3.7.0",
19+
"eslint-plugin-standard": "^3.1.0",
20+
"jest": "^22.4.3",
21+
"prettier": "^1.12.1",
22+
"svelte": "^2.5.1"
23+
},
24+
"peerDependencies": {
25+
"svelte": "^2.5.1"
26+
},
27+
"optionalDependencies": {
28+
"coffeescript": "^2.3.0",
29+
"less": "^3.0.4",
30+
"node-sass": "^4.9.0",
31+
"pug": "^2.0.3",
32+
"stylus": "^0.54.5"
33+
},
34+
"dependencies": {
35+
"strip-indent": "^2.0.0"
36+
}
37+
}

src/index.js

+144
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
const { readFileSync } = require('fs')
2+
const { dirname, resolve } = require('path')
3+
const stripIndent = require('strip-indent')
4+
5+
const { findPackageJson, getLanguage, runPreprocessor } = require('./utils.js')
6+
7+
const throwError = (msg) => { throw new Error(`[svelte-smart-preprocess] ${msg}`) }
8+
const throwUnsupportedError = (lang, filename) =>
9+
throwError(`Unsupported script language '${lang}' in file '${filename}'`)
10+
11+
module.exports = (languages = {}) => {
12+
return {
13+
markup: ({content, filename}) => {
14+
const templateMatch = content.match(/(<template([\s\S]*?)>([\s\S]*?)<\/template>)/)
15+
16+
/** If no <template> was found, just return the original markup */
17+
if (!templateMatch) {
18+
return { code: content }
19+
}
20+
21+
let [, wholeTemplateTag, unparsedAttrs, templateCode] = templateMatch
22+
23+
/** Transform the <template> attributes into a consumable object */
24+
unparsedAttrs = unparsedAttrs.trim()
25+
const attributes = unparsedAttrs.length > 0
26+
? unparsedAttrs.split(' ').reduce((acc, entry) => {
27+
const [key, value] = entry.split('=')
28+
acc[key] = value.replace(/['"]/g, '')
29+
return acc
30+
}, {})
31+
: {}
32+
33+
const lang = getLanguage(attributes, 'html')
34+
const componentDir = dirname(filename)
35+
36+
/** If src="" is allowed and was defined, let's get the referenced file content */
37+
if (attributes.src) {
38+
templateCode = readFileSync(resolve(componentDir, attributes.src)).toString()
39+
}
40+
41+
/** If language is HTML, just remove the <template></template> tags */
42+
if (lang === 'html') {
43+
return {
44+
code: content.replace(wholeTemplateTag, templateCode)
45+
}
46+
}
47+
48+
if (languages[lang]) {
49+
const processedContent = runPreprocessor(lang,
50+
languages[lang],
51+
stripIndent(templateCode),
52+
filename
53+
)
54+
55+
/** It may return a promise, let's check for that */
56+
if (Promise.resolve(processedContent) === processedContent) {
57+
return processedContent.then(({ code }) => {
58+
return {
59+
code: content.replace(wholeTemplateTag, code)
60+
}
61+
})
62+
}
63+
64+
/** Remove the <template> tag with the actual template code */
65+
return {
66+
code: content.replace(wholeTemplateTag, processedContent.code)
67+
}
68+
}
69+
70+
throwUnsupportedError(lang, filename)
71+
},
72+
script: ({ content = '', attributes, filename }) => {
73+
const lang = getLanguage(attributes, 'javascript')
74+
const componentDir = dirname(filename)
75+
76+
/** If src="" is allowed and was defined, let's get the referenced file content */
77+
if (attributes.src) {
78+
content = readFileSync(resolve(componentDir, attributes.src)).toString()
79+
}
80+
81+
if (lang === 'javascript') {
82+
return { code: content }
83+
}
84+
85+
if (languages[lang]) {
86+
return runPreprocessor(lang, languages[lang], content, filename)
87+
}
88+
89+
throwUnsupportedError(lang, filename)
90+
},
91+
style: ({ content = '', attributes, filename }) => {
92+
const lang = getLanguage(attributes, 'css')
93+
const componentDir = dirname(filename)
94+
95+
/** If src="" is allowed and was defined, let's get the referenced file content */
96+
if (attributes.src) {
97+
content = readFileSync(resolve(componentDir, attributes.src)).toString()
98+
}
99+
100+
if (lang === 'css') {
101+
return { code: content }
102+
}
103+
104+
/** If the build step is supporting the used language, run it's preprocessor */
105+
if (languages[lang]) {
106+
return runPreprocessor(lang, languages[lang], content, filename)
107+
} else {
108+
throwUnsupportedError(lang, filename)
109+
/**
110+
* If including a uncompiled svelte component which uses a not supported style language,
111+
* search for it's package.json to see if there's a valid css file defined with 'svelte.style' or 'style'.
112+
* */
113+
const { data: pkgData, filename: pkgFilepath } = findPackageJson(
114+
componentDir
115+
)
116+
117+
/** Found any package data? Is it a svelte component? */
118+
if (pkgData && pkgData.svelte) {
119+
const pkgDir = dirname(pkgFilepath)
120+
const svelteStyle = pkgData['svelte.style'] || pkgData.style
121+
122+
/**
123+
* If there's a valid style definition, get the defined file's content.
124+
*
125+
* TODO - This is broken when the css has global styles. Disabled for now.
126+
* */
127+
if (false && svelteStyle) { // eslint-disable-line
128+
// log(
129+
// 'info',
130+
// `Using precompiled css (${svelteStyle}) in component "${
131+
// pkgData.name
132+
// }"`
133+
// )
134+
content = readFileSync(resolve(pkgDir, svelteStyle))
135+
.toString()
136+
.replace(/\.svelte-\w{4,7}/, '')
137+
} else {
138+
throwUnsupportedError(lang, filename)
139+
}
140+
}
141+
}
142+
}
143+
}
144+
}

src/langs/coffeescript.js

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
const coffeescript = require('coffeescript')
2+
3+
module.exports = function (content, filename, opts) {
4+
const { js: code, sourceMap: map } = coffeescript.compile(content, {
5+
filename,
6+
sourceMap: true,
7+
...opts
8+
})
9+
10+
return { code, map }
11+
}

src/langs/less.js

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
const less = require('less/lib/less-node')
2+
3+
module.exports = function (content, filename, opts) {
4+
return less.render(content, { sourceMap: {}, ...opts })
5+
.then(output => ({ code: output.css, map: output.map }))
6+
}

src/langs/pug.js

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
const pug = require('pug')
2+
3+
module.exports = function (content, filename, opts) {
4+
const code = pug.render(content, opts)
5+
6+
return { code }
7+
}

src/langs/scss.js

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
const sass = require('node-sass')
2+
3+
module.exports = function (content, filename, opts = {
4+
includePaths: ['node_modules']
5+
}) {
6+
return new Promise((resolve, reject) => {
7+
sass.render(
8+
{
9+
data: content,
10+
sourceMap: true,
11+
outFile: filename + '.css',
12+
...opts
13+
},
14+
(err, result) => {
15+
if (err) return reject(err)
16+
17+
resolve({
18+
code: result.css.toString(),
19+
map: result.map.toString()
20+
})
21+
}
22+
)
23+
})
24+
}

src/langs/stylus.js

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
const stylus = require('stylus')
2+
3+
module.exports = function (content, filename, opts = {
4+
paths: ['node_modules']
5+
}) {
6+
return new Promise((resolve, reject) => {
7+
const style = stylus(content, {
8+
filename,
9+
sourcemap: true,
10+
...opts
11+
})
12+
style.render((err, css) => {
13+
if (err) reject(err)
14+
15+
resolve({
16+
code: css,
17+
map: style.sourcemap
18+
})
19+
})
20+
})
21+
}

0 commit comments

Comments
 (0)