diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 0000000..03f2432 --- /dev/null +++ b/.eslintrc @@ -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"] +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2cc5f6a --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.idea +node_modules +package-lock.json diff --git a/README.md b/README.md new file mode 100644 index 0000000..439b65c --- /dev/null +++ b/README.md @@ -0,0 +1,50 @@ +npm package +node compatility + +# ⚡️🌿 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 + + +``` +```html + + {{ title }} +``` +```html + +{ + "template": "path/to/template.twig", + "title": "Hello world" +} +``` + +### Requirements + +- [Node.js LTS (16.x)](https://nodejs.org/en/download/) diff --git a/index.js b/index.js new file mode 100644 index 0000000..2476c55 --- /dev/null +++ b/index.js @@ -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('