Skip to content

Commit

Permalink
feat: added plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
lubomirblazekcz committed Aug 13, 2022
0 parents commit b4cdb69
Show file tree
Hide file tree
Showing 5 changed files with 244 additions and 0 deletions.
14 changes: 14 additions & 0 deletions .eslintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"extends": "standard",
"rules": {
"indent": ["error", 4],
"no-new": "off",
"space-before-function-paren": ["error", "never"],
"import/extensions": ["error", "ignorePackages"],
"multiline-ternary": ["error", "never"]
},
"env": {
"browser": true
},
"ignorePatterns": ["src", "public"]
}
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.idea
node_modules
package-lock.json
50 changes: 50 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<a href="https://npmjs.com/package/@vituum/vite-plugin-twig"><img src="https://img.shields.io/npm/v/@vituum/vite-plugin-twig.svg" alt="npm package"></a>
<a href="https://nodejs.org/en/about/releases/"><img src="https://img.shields.io/node/v/@vituum/vite-plugin-twig.svg" alt="node compatility"></a>

# ⚡️🌿 ViteTwig

```js
export default {
plugins: [
twig({
filters: {},
functions: {},
extensions: [],
namespaces: {},
data: '*.json',
globals: {
template: 'path/to/template.twig'
},
filetypes: {
html: /.(json.html|twig.json.html|twig.html)$/,
json: /.(json.twig.html)$/
}
})
]
}
```

```html
<!-- index.html -->
<script type="application/json">
{
"template": "path/to/template.twig",
"title": "Hello world"
}
</script>
```
```html
<!-- index.twig.html -->
{{ title }}
```
```html
<!-- index.json.html or index.twig.json.html -->
{
"template": "path/to/template.twig",
"title": "Hello world"
}
```

### Requirements

- [Node.js LTS (16.x)](https://nodejs.org/en/download/)
150 changes: 150 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
import { dirname, extname, resolve, relative } from 'path'
import fs from 'fs'
import process from 'node:process'
import FastGlob from 'fast-glob'
import lodash from 'lodash'
import Twig from 'twig'
import chalk from 'chalk'
import { fileURLToPath } from 'url'

const { name } = JSON.parse(fs.readFileSync(resolve(dirname((fileURLToPath(import.meta.url))), 'package.json')).toString())
const defaultOptions = {
filters: {},
functions: {},
extensions: [],
globals: {},
namespaces: {},
data: '',
filetypes: {
html: /.(json.html|twig.json.html|twig.html)$/,
json: /.(json.twig.html)$/
}
}

function processData(paths, data = {}) {
let context = {}

lodash.merge(context, data)

FastGlob.sync(paths).forEach(entry => {
const path = resolve(process.cwd(), entry)

context = lodash.merge(context, JSON.parse(fs.readFileSync(path).toString()))
})

return context
}

const renderTemplate = (filename, content, options) => {
const output = {}
const context = processData(options.data, options.globals)

Twig.cache(false)

if (!Array.isArray(options.extensions)) {
throw new TypeError('\'extensions\' needs to be an array of functions!')
} else {
options.extensions.forEach((name) => {
Twig.extend(name)
})
}

Object.keys(options.functions).forEach(name => {
if (typeof options.functions[name] !== 'function') {
throw new TypeError(`${name} needs to be a function!`)
}

Twig.extendFunction(name, options.functions[name])
})

Object.keys(options.filters).forEach(name => {
if (typeof options.filters[name] !== 'function') {
throw new TypeError(`${name} needs to be a function!`)
}

Twig.extendFilter(name, options.filters[name])
})

if (
filename.endsWith('.json.html') ||
filename.endsWith('.json')
) {
lodash.merge(context, JSON.parse(fs.readFileSync(filename).toString()))

content = '{% include template %}'
filename = options.root

context.template = relative(process.cwd(), context.template)
} else if (fs.existsSync(filename + '.json')) {
lodash.merge(context, JSON.parse(fs.readFileSync(filename + '.json').toString()))
}

try {
output.content = Twig.twig({
async: true,
data: content,
path: filename,
namespaces: options.namespaces,
rethrow: true
}).render(context)
} catch (error) {
output.error = error
}

return output
}

const plugin = (options = {}) => {
options = lodash.merge(defaultOptions, options)

return {
name,
config: ({ root }) => {
options.root = root
},
transformIndexHtml: {
enforce: 'pre',
async transform(content, { path, filename, server }) {
if (
!options.filetypes.html.test(path) &&
!options.filetypes.json.test(path) &&
!content.startsWith('<script type="application/json"')
) {
return content
}

if (content.startsWith('<script type="application/json"') && !content.includes('data-format="twig"')) {
return content
}

const render = renderTemplate(filename, content, options)

if (render.error) {
if (!server) {
console.error(chalk.red(render.error))
return
}

server.ws.send({
type: 'error',
err: {
message: render.error.message,
plugin: name
}
})

return '<html style="background: #222"><head></head><body></body></html>'
}

return render.content
}
},
handleHotUpdate({ file, server }) {
if (extname(file) === '.twig' || extname(file) === '.html' || extname(file) === '.json') {
server.ws.send({ type: 'full-reload' })
}
}
}
}

export default plugin
27 changes: 27 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"name": "@vituum/vite-plugin-twig",
"version": "0.1.0",
"type": "module",
"main": "index.js",
"dependencies": {
"twig": "^1.15.4",
"lodash": "^4.17.21",
"chalk": "^5.0.1",
"fast-glob": "^3.2.11"
},
"devDependencies": {
"eslint": "^8.21.0",
"eslint-config-standard": "^17.0.0"
},
"files": [
"index.js"
],
"engines": {
"node": ">=16.0.0",
"npm": ">=8.0.0"
},
"repository": {
"type": "git",
"url": "git+https://github.com/vituum/vite-plugin-twig.git"
}
}

0 comments on commit b4cdb69

Please sign in to comment.