Skip to content

Commit

Permalink
fully functional
Browse files Browse the repository at this point in the history
  • Loading branch information
gVguy committed Oct 23, 2021
0 parents commit e446c5b
Show file tree
Hide file tree
Showing 14 changed files with 4,734 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.DS_Store
node_modules/
dist/
33 changes: 33 additions & 0 deletions components/depth/two.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<template>
<div>
<h1>{{text}}</h1>
I have two style blocks:<br>
one scoped, that makes my background pink<br>
and the other is not,<br>
it makes all headers' <code>font-family</code> <code>sans-serif</code>
</div>
</template>

<script>
export default {
name: 'DeeperTwo',
data() {
return {
text: 'Hello from a level deeper Component!'
}
},
}
</script>

<style scoped>
div {
background: pink;
}
</style>

<style>
h1 {
font-family: sans-serif;
}
</style>
33 changes: 33 additions & 0 deletions components/one.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<template>
<div class="container">
<h1>{{text}}</h1>
I have class <code>.container</code><br>
with a silver background defined in scoped styles<br>
<br>
I also host two other components:
<Two />
<DeeperTwo />
</div>
</template>

<script>
import Two from './two.vue'
import DeeperTwo from './depth/two.vue'
export default {
name: 'One',
components: { Two, DeeperTwo },
data() {
return {
text: 'Hello from component One'
}
},
}
</script>

<style scoped>
.container {
background: silver;
}
</style>
40 changes: 40 additions & 0 deletions components/two.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<template>
</template>

<script>
export default {
name: 'Two',
template: `<div class="container">
<h1>{{text}}</h1>
I have template defined inside script<br>
with <code>template: \` ... \`</code><br>
<span>This span is colored purple with scoped styles</span><br>
<br>
I also have a div with class <code>.container</code><br>
But its color is gold, because my scoped styles said so<br>
<br>
My styles are also in SCSS by the way
</div>`
data() {
return {
text: 'Hello from component Two!'
}
},
}
</script>

<style scoped lang="scss">
.container {
background: gold;
h1 {
color: white;
&:hover {
background: black;
}
}
}
span {
color: purple;
}
</style>
235 changes: 235 additions & 0 deletions convert.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,235 @@
#!/usr/bin/env node

const glob = require('glob')
const fs = require('fs')
const compiler = require('vue-template-compiler')
const sass = require('node-sass')
const postcss = require('postcss')
const path = require('path')
const minimist = require('minimist')
const htmlparser = require('node-html-parser')


// options

const args = minimist(process.argv.slice(2))
const opts = {
tab: ' ',
ignore: ['node_modules/**'],
noscope: false,
dest: 'dist/',
}

opts.tab = args.tab != undefined ? args.tab : opts.tab
opts.ignore = args.ignore ? opts.ignore.concat(args.ignore.split(',')) : opts.ignore
opts.dest = args.dest
? args.dest.slice(-1) != '/'
? args.dest+'/'
: args.dest
: opts.dest
opts.noscope = args.noscope ? true : false



const files = glob.sync('**/*.vue', { ignore: opts.ignore })


files.forEach(file => {

const pathParse = path.parse(file)

const scopeSelectors = []

let warnings = []

fs.readFile(file, 'utf8', async (err, content) => {

if (err) return console.error(err)


// parse

const parsed = compiler.parseComponent(content)


// extract


// script & style

let script = parsed.script ? parsed.script.content.trim() : ''
let styles = '' // styles will be filled later

// template

let template = ''
if (parsed.template && parsed.template.content.trim()) {
template = parsed.template.content.trim()
}
else if (script) {
// there is no template block, but there is script
// maybe template is in js
const match = script.match(/(.|\n)*template:\s*([\`'"])/m)
if (match) {
// template is in js
// extract it
template = script.replace(match[0], '')
const quote = match[0].slice(-1)
let regex = new RegExp('(?<!\\\\)\\'+quote+'(.|\\n)*','m')
template = template.replace(regex, '')

// remove template from script (it will get injected back later)
regex = new RegExp('template:\\s*'+quote+'(.|\\n)*(?<!\\\\)'+quote+',?\\s*', 'm')
script = script.replace(regex, '')
}
}


// process


// styles

if (parsed.styles && parsed.styles.length) {

for (let parsedStyle of parsed.styles) {
let style = parsedStyle.content.trim()

if (parsedStyle.lang && parsedStyle.lang == 'scss') {
// scss -> css
style = sass.renderSync({
data: style,
outputStyle: 'expanded'
}).css.toString()
}


if (parsedStyle.scoped && template && !opts.noscope) {
// scoped

// generate scope name from filename
const scope = file
.replace(/\.vue$/, '')
.replace(/[\/.]/g, '-')


// add scope to css selectors
style = await postcss().use(root => {
root.walkRules(rule => {
scopeSelectors.push(rule.selector)
const selectors = rule.selector.match(/(^|[\.#\s])[\w\-]+/g)
selectors.forEach(s => {
rule.selector = rule.selector.replace(s, s+'[data-scope-'+scope+']')
})
})
}).process(style, { from: undefined }).then(result => result.css)


// add scope to template items

const html = htmlparser.parse(template)

scopeSelectors.forEach(selector => {
// only add scope attribute to elements that are styled from scoped css
const els = html.querySelectorAll(selector)

for (let el of els) {
for (let a of Object.keys(el.attributes)) {
if (a.includes('data-scope-')) {
el.removeAttribute(a)
}
}
el.setAttribute('data-scope-'+scope, '')
}

})

template = html.toString()

// remove ="" for prettiness
const regex = new RegExp('(data-scope-'+scope+')=""','g')
template = template.replace(regex, '$1')

} else if (!template && !opts.noscope) {
warnings.push('scoped style, but no template')
}

// join all styles
styles += style + '\n'

}

if (script) {
// add style attachment to script
script += '\n\n// attach styles\nfetch(\''+pathParse.dir+'/'+pathParse.name+'.css\').then(res => res.text()).then(style => document.head.insertAdjacentHTML(\'beforeend\', \'<style>\'+style+\'</style>\'))'
}

} else {
warnings.push('no style')
}


// template -> script

if (template && script) {

// backslash any unbackslashed occurancies of `
template = template.trim().replace(/(?<!\\)`/g, '\`')

const templateString = 'template: `\n' + template + '`,'

const match = script.match(/(export\sdefault\s*\{)/)
if (match) {
// there is a default export, append template to it
script = script.replace(
match[0], match[0] + '\n' + opts.tab + templateString
)
} else {
warnings.push('can\'t find default export')
}

} else {
warnings.push('no script or template')
}


// rewrite imports
if (script) {
script = script.replace(/import(.*)\.vue(['"])/g, 'import$1.js$2')
}


// write files

const dir = opts.dest + pathParse.dir
const outPath = dir + '/' + pathParse.name

fs.mkdir(dir, { recursive: true }, (err) => {
if (err) return console.log(err)

if (script) {
fs.writeFile(outPath+'.js', script, err => {
if (err) return console.log(err)
})
}

if (styles) {
fs.writeFile(outPath+'.css', styles, err => {
if (err) return console.log(err)
})
}
})


// log warnings

if (warnings.length) {
warnings.forEach(w => console.log('\x1b[33m%s\x1b[0m', 'WARN! '+file+' : '+ w))
}



})
})


6 changes: 6 additions & 0 deletions example/components/depth/two.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
div[data-scope-components-depth-two] {
background: pink;
}
h1 {
font-family: sans-serif;
}
19 changes: 19 additions & 0 deletions example/components/depth/two.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
export default {
template: `
<div data-scope-components-depth-two>
<h1>{{text}}</h1>
I have two style blocks:<br>
one scoped, that makes my background pink<br>
and the other is not,<br>
it makes all headers' <code>font-family</code> <code>sans-serif</code>
</div>`,
name: 'DeeperTwo',
data() {
return {
text: 'Hello from a level deeper Component!'
}
},
}

// attach styles
fetch('components/depth/two.css').then(res => res.text()).then(style => document.head.insertAdjacentHTML('beforeend', '<style>'+style+'</style>'))
3 changes: 3 additions & 0 deletions example/components/one.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.container[data-scope-components-one] {
background: silver;
}
Loading

0 comments on commit e446c5b

Please sign in to comment.