@vituum/vite-plugin-pug 0.1.5 → 1.0.0-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -7,46 +7,29 @@
7
7
  import pug from '@vituum/vite-plugin-pug'
8
8
 
9
9
  export default {
10
- plugins: [
11
- pug({
12
- reload: true,
13
- root: null,
14
- filters: {},
15
- data: '*.json',
16
- globals: {
17
- template: 'path/to/template.pug'
18
- },
19
- filetypes: {
20
- html: /.(json.html|pug.json.html|pug.html)$/,
21
- json: /.(json.pug.html)$/
22
- },
23
- pug: {}
24
- })
25
- ]
10
+ plugins: [
11
+ pug()
12
+ ],
13
+ build: {
14
+ rollupOptions: {
15
+ input: ['index.pug.html']
16
+ }
17
+ }
26
18
  }
27
19
  ```
28
20
 
29
- Read the [docs](https://vituum.dev/config/integrations-options.html#vituum-pug) to learn more about the plugin options.
21
+ * Read the [docs](https://vituum.dev/plugins/pug.html) to learn more about the plugin options.
22
+ * Use with [Vituum](https://vituum.dev) to get full functionality.
30
23
 
31
24
  ## Basic usage
32
25
 
33
26
  ```html
34
- <!-- index.html -->
35
- <script type="application/json" data-format="pug">
36
- {
37
- "template": "path/to/template.pug",
38
- "title": "Hello world"
39
- }
40
- </script>
41
- ```
42
- or
43
- ```html
44
- <!-- index.pug.html -->
27
+ <!-- index.pug -->
45
28
  include /path/to/template.pug
46
29
  ```
47
30
  or
48
31
  ```html
49
- <!-- index.json.html or index.pug.json.html -->
32
+ <!-- index.json -->
50
33
  {
51
34
  "template": "path/to/template.pug",
52
35
  "title": "Hello world"
@@ -55,5 +38,5 @@ or
55
38
 
56
39
  ### Requirements
57
40
 
58
- - [Node.js LTS (16.x)](https://nodejs.org/en/download/)
59
- - [Vite](https://vitejs.dev/) or [Vituum](https://vituum.dev/)
41
+ - [Node.js LTS (18.x)](https://nodejs.org/en/download/)
42
+ - [Vite](https://vitejs.dev/)
package/index.js CHANGED
@@ -1,138 +1,148 @@
1
- import { dirname, resolve, relative } from 'path'
1
+ import { resolve, relative } from 'path'
2
2
  import fs from 'fs'
3
3
  import process from 'node:process'
4
- import FastGlob from 'fast-glob'
5
4
  import lodash from 'lodash'
6
5
  import pug from 'pug'
7
- import chalk from 'chalk'
8
- import { fileURLToPath } from 'url'
6
+ import { getPackageInfo, merge, pluginBundle, pluginError, pluginReload, processData } from 'vituum/utils/common.js'
7
+ import { renameBuildEnd, renameBuildStart } from 'vituum/utils/build.js'
8
+ import { minimatch } from 'minimatch'
9
9
 
10
- const { name } = JSON.parse(fs.readFileSync(resolve(dirname((fileURLToPath(import.meta.url))), 'package.json')).toString())
10
+ const { name } = getPackageInfo(import.meta.url)
11
+
12
+ /**
13
+ * @type {import('@vituum/vite-plugin-pug/types').PluginUserConfig}
14
+ */
11
15
  const defaultOptions = {
12
16
  reload: true,
13
17
  root: null,
14
18
  filters: {},
15
- globals: {},
16
- data: '',
17
- filetypes: {
18
- html: /.(json.html|pug.json.html|pug.html)$/,
19
- json: /.(json.pug.html)$/
19
+ globals: {
20
+ format: 'pug'
20
21
  },
21
- pug: {}
22
- }
23
-
24
- function processData(paths, data = {}) {
25
- let context = {}
26
-
27
- lodash.merge(context, data)
28
-
29
- const normalizePaths = Array.isArray(paths) ? paths.map(path => path.replace(/\\/g, '/')) : paths.replace(/\\/g, '/')
30
-
31
- FastGlob.sync(normalizePaths).forEach(entry => {
32
- const path = resolve(process.cwd(), entry)
33
-
34
- context = lodash.merge(context, JSON.parse(fs.readFileSync(path).toString()))
35
- })
36
-
37
- return context
22
+ data: ['src/data/**/*.json'],
23
+ formats: ['pug', 'json.pug', 'json'],
24
+ ignoredPaths: [],
25
+ options: {}
38
26
  }
39
27
 
40
- const renderTemplate = async(filename, content, options) => {
28
+ const renderTemplate = async ({ filename, server, root }, content, options) => {
29
+ const initialFilename = filename.replace('.html', '')
41
30
  const output = {}
42
- const context = options.data ? processData(options.data, options.globals) : options.globals
31
+ const context = options.data
32
+ ? processData({
33
+ paths: options.data,
34
+ root
35
+ }, options.globals)
36
+ : options.globals
43
37
 
44
- const isJson = filename.endsWith('.json.html') || filename.endsWith('.json')
45
- const isHtml = filename.endsWith('.html') && !options.filetypes.html.test(filename) && !options.filetypes.json.test(filename) && !isJson
46
-
47
- if (isJson || isHtml) {
48
- lodash.merge(context, isHtml ? content : JSON.parse(fs.readFileSync(filename).toString()))
38
+ if (initialFilename.endsWith('.json')) {
39
+ lodash.merge(context, JSON.parse(content))
49
40
 
50
41
  output.template = true
51
42
 
52
43
  if (typeof context.template === 'undefined') {
53
- console.error(chalk.red(name + ' template must be defined - ' + filename))
44
+ const error = `${name}: template must be defined for file ${initialFilename}`
45
+
46
+ return new Promise((resolve) => {
47
+ output.error = error
48
+ resolve(output)
49
+ })
54
50
  }
55
51
 
56
52
  context.template = relative(process.cwd(), context.template).startsWith(relative(process.cwd(), options.root)) ? resolve(process.cwd(), context.template) : resolve(options.root, context.template)
57
- } else if (fs.existsSync(filename + '.json')) {
58
- lodash.merge(context, JSON.parse(fs.readFileSync(filename + '.json').toString()))
53
+ } else if (fs.existsSync(`${initialFilename}.json`)) {
54
+ lodash.merge(context, JSON.parse(fs.readFileSync(`${initialFilename}.json`).toString()))
59
55
  }
60
56
 
61
- try {
62
- const template = pug.compileFile(output.template ? context.template : filename, Object.assign(options.pug, {
63
- basedir: options.root,
64
- filters: options.filters
65
- }))
57
+ return new Promise((resolve) => {
58
+ try {
59
+ const template = pug.compileFile(output.template ? context.template : server ? initialFilename : filename, Object.assign(options.options, {
60
+ basedir: options.root,
61
+ filters: options.filters
62
+ }))
66
63
 
67
- output.content = template(context)
68
- } catch (error) {
69
- output.error = error
70
- }
64
+ output.content = template(context)
65
+
66
+ resolve(output)
67
+ } catch (error) {
68
+ output.error = error
71
69
 
72
- return output
70
+ resolve(output)
71
+ }
72
+ })
73
73
  }
74
74
 
75
+ /**
76
+ * @param {import('@vituum/vite-plugin-pug/types').PluginUserConfig} options
77
+ * @returns [import('vite').Plugin]
78
+ */
75
79
  const plugin = (options = {}) => {
76
- options = lodash.merge(defaultOptions, options)
80
+ let resolvedConfig
81
+ let userEnv
77
82
 
78
- return {
83
+ options = merge(defaultOptions, options)
84
+
85
+ return [{
79
86
  name,
80
- config: ({ root }) => {
87
+ config (userConfig, env) {
88
+ userEnv = env
89
+ },
90
+ configResolved (config) {
91
+ resolvedConfig = config
92
+
81
93
  if (!options.root) {
82
- options.root = root
94
+ options.root = config.root
83
95
  }
84
96
  },
85
- transformIndexHtml: {
86
- enforce: 'pre',
87
- async transform(content, { path, filename, server }) {
88
- path = path.replace('?raw', '')
89
- filename = filename.replace('?raw', '')
97
+ buildStart: async () => {
98
+ if (userEnv.command !== 'build') {
99
+ return
100
+ }
90
101
 
102
+ await renameBuildStart(resolvedConfig.build.rollupOptions.input, options.formats)
103
+ },
104
+ buildEnd: async () => {
105
+ if (userEnv.command !== 'build') {
106
+ return
107
+ }
108
+
109
+ await renameBuildEnd(resolvedConfig.build.rollupOptions.input, options.formats)
110
+ },
111
+ transformIndexHtml: {
112
+ order: 'pre',
113
+ async transform (content, { path, filename, server }) {
91
114
  if (
92
- !options.filetypes.html.test(path) &&
93
- !options.filetypes.json.test(path) &&
94
- !content.startsWith('<script type="application/json" data-format="pug"')
115
+ !options.formats.find(format => filename.replace('.html', '').endsWith(format)) ||
116
+ (filename.replace('.html', '').endsWith('.json') && !content.startsWith('{'))
95
117
  ) {
96
118
  return content
97
119
  }
98
120
 
99
- if (content.startsWith('<script type="application/json" data-format="pug"')) {
100
- const matches = content.matchAll(/<script\b[^>]*data-format="(?<format>[^>]+)"[^>]*>(?<data>[\s\S]+?)<\/script>/gmi)
121
+ if (
122
+ (filename.replace('.html', '').endsWith('.json') && content.startsWith('{')) &&
123
+ (JSON.parse(content)?.format && !options.formats.includes(JSON.parse(content)?.format))
124
+ ) {
125
+ return content
126
+ }
101
127
 
102
- for (const match of matches) {
103
- content = JSON.parse(match.groups.data)
104
- }
128
+ if (options.ignoredPaths.find(ignoredPath => minimatch(path.replace('.html', ''), ignoredPath) === true)) {
129
+ return content
105
130
  }
106
131
 
107
- const render = await renderTemplate(filename, content, options)
108
-
109
- if (render.error) {
110
- if (!server) {
111
- console.error(chalk.red(render.error))
112
- return
113
- }
114
-
115
- setTimeout(() => server.ws.send({
116
- type: 'error',
117
- err: {
118
- message: render.error.message,
119
- plugin: name
120
- }
121
- }), 50)
132
+ const render = await renderTemplate({ filename, server, root: resolvedConfig.root }, content, options)
133
+ const renderError = pluginError(render.error, server, name)
134
+
135
+ if (renderError && server) {
136
+ return
137
+ } else if (renderError) {
138
+ return renderError
122
139
  }
123
140
 
124
141
  return render.content
125
142
  }
126
143
  },
127
- handleHotUpdate({ file, server }) {
128
- if (
129
- (typeof options.reload === 'function' && options.reload(file)) ||
130
- (options.reload && (options.filetypes.html.test(file) || options.filetypes.json.test(file)))
131
- ) {
132
- server.ws.send({ type: 'full-reload' })
133
- }
134
- }
135
- }
144
+ handleHotUpdate: ({ file, server }) => pluginReload({ file, server }, options)
145
+ }, pluginBundle(options.formats)]
136
146
  }
137
147
 
138
148
  export default plugin
package/package.json CHANGED
@@ -1,24 +1,37 @@
1
1
  {
2
2
  "name": "@vituum/vite-plugin-pug",
3
- "version": "0.1.5",
3
+ "version": "1.0.0-alpha.2",
4
4
  "type": "module",
5
5
  "main": "index.js",
6
+ "scripts": {
7
+ "tsc": "tsc",
8
+ "eslint": "eslint '**/*.js' --fix"
9
+ },
6
10
  "dependencies": {
7
11
  "pug": "^3.0.2",
8
12
  "lodash": "^4.17.21",
9
- "chalk": "^5.2.0",
10
- "fast-glob": "^3.2.12"
13
+ "fast-glob": "^3.2.12",
14
+ "vituum": "^1.0.0-alpha.23",
15
+ "minimatch": "^9.0.1"
11
16
  },
12
17
  "devDependencies": {
13
- "eslint": "^8.29.0",
14
- "eslint-config-standard": "^17.0.0"
18
+ "@types/node": "^20.3.1",
19
+ "eslint": "^8.43.0",
20
+ "eslint-config-standard": "^17.1.0",
21
+ "typescript": "^5.1.3",
22
+ "vite": "^4.3.9"
15
23
  },
16
24
  "files": [
17
- "index.js"
25
+ "index.js",
26
+ "types"
18
27
  ],
28
+ "exports": {
29
+ ".": "./index.js",
30
+ "./types": "./types/*"
31
+ },
19
32
  "engines": {
20
- "node": ">=16.0.0",
21
- "npm": ">=8.0.0"
33
+ "node": ">=18.0.0",
34
+ "npm": ">=9.0.0"
22
35
  },
23
36
  "repository": {
24
37
  "type": "git",
@@ -0,0 +1,37 @@
1
+ interface PugOptions {
2
+ /** The name of the file being compiled. Used in exceptions, and required for relative includes and extends. Defaults to 'Pug'. */
3
+ filename?: string | undefined,
4
+ /** The root directory of all absolute inclusion. */
5
+ basedir?: string | undefined,
6
+ /** If the doctype is not specified as part of the template, you can specify it here. It is sometimes useful to get self-closing tags and remove mirroring of boolean attributes; see doctype documentation for more information. */
7
+ doctype?: string | undefined,
8
+ /** Adds whitespace to the resulting HTML to make it easier for a human to read using ' ' as indentation. If a string is specified, that will be used as indentation instead (e.g. '\t'). Defaults to false. */
9
+ pretty?: boolean | string | undefined,
10
+ /** Hash table of custom filters. Defaults to undefined. */
11
+ filters?: any,
12
+ /** Use a self namespace to hold the locals. It will speed up the compilation, but instead of writing variable you will have to write self.variable to access a property of the locals object. Defaults to false. */
13
+ self?: boolean | undefined,
14
+ /** If set to true, the tokens and function body are logged to stdout. */
15
+ debug?: boolean | undefined,
16
+ /** If set to true, the function source will be included in the compiled template for better error messages (sometimes useful in development). It is enabled by default unless used with Express in production mode. */
17
+ compileDebug?: boolean | undefined,
18
+ /** Add a list of global names to make accessible in templates. */
19
+ globals?: Array<string> | undefined,
20
+ /** If set to true, compiled functions are cached. filename must be set as the cache key. Only applies to render functions. Defaults to false. */
21
+ cache?: boolean | undefined,
22
+ /** Inline runtime functions instead of require-ing them from a shared version. For compileClient functions, the default is true so that one does not have to include the runtime. For all other compilation or rendering types, the default is false. */
23
+ inlineRuntimeFunctions?: boolean | undefined,
24
+ /** The name of the template function. Only applies to compileClient functions. Defaults to 'template'. */
25
+ name?: string | undefined
26
+ }
27
+
28
+ export interface PluginUserConfig {
29
+ reload?: boolean | Function
30
+ root?: string
31
+ filters?: Object
32
+ globals?: Object
33
+ data?: string | string[]
34
+ formats?: string[]
35
+ ignoredPaths?: string[]
36
+ options?: PugOptions
37
+ }