Skip to content

Commit 82145e2

Browse files
authored
Merge pull request #7 from mbland/strict-typing
Add strict type checking via TypeScript
2 parents 4400723 + eb5b9a8 commit 82145e2

14 files changed

+515
-84
lines changed

.eslintrc

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
2-
"env" : {
2+
"env": {
33
"node": true,
4-
"es2023" : true
4+
"es2023": true
55
},
66
"parserOptions": {
77
"ecmaVersion": "latest",
@@ -14,7 +14,7 @@
1414
],
1515
"extends": [
1616
"eslint:recommended",
17-
"plugin:jsdoc/recommended"
17+
"plugin:jsdoc/recommended-typescript-flavor-error"
1818
],
1919
"overrides": [
2020
{
@@ -25,7 +25,7 @@
2525
]
2626
}
2727
],
28-
"rules" : {
28+
"rules": {
2929
"@stylistic/js/comma-dangle": [
3030
"error", "never"
3131
],

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,6 @@ node_modules/
1010
out/
1111
pnpm-debug.log
1212
tmp/
13+
types/
1314
*.log
15+
*.tgz

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
_**Status**: I've still got a bit of work to do before publishing v1.0.0. I need
66
to add tests based on the mbland/tomcat-servlet-testing-example project from
77
whence this came and add more documentation. I plan to finish this by
8-
2024-01-08._
8+
2024-01-11._
99

1010
Source: <https://github.com/mbland/rollup-plugin-handlebars-precompiler>
1111

ci/vitest.config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { defineConfig, mergeConfig } from 'vitest/config'
2-
import baseConfig from '../vitest.config'
2+
import baseConfig from '../vitest.config.js'
33

44
export default mergeConfig(baseConfig, defineConfig({
55
test: {

index.js

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,24 +37,56 @@
3737
*/
3838

3939
import PluginImpl, { PLUGIN_NAME } from './lib/index.js'
40+
// eslint-disable-next-line no-unused-vars
41+
import { PluginOptions, Transform } from './lib/types.js'
4042

4143
/**
4244
* A Rollup plugin object for precompiling Handlebars templates.
4345
* @module rollup-plugin-handlebars-precompiler
4446
*/
4547

48+
/**
49+
* @typedef {object} RollupPlugin
50+
* @property {string} name - plugin name
51+
* @property {Function} resolveId - resolves the plugin's own import ID
52+
* @property {Function} load - emits the plugin's helper module code
53+
* @property {Function} transform - emits JavaScript code compiled from
54+
* Handlebars templates
55+
* @see https://rollupjs.org/plugin-development/
56+
*/
57+
4658
/**
4759
* Returns a Rollup plugin object for precompiling Handlebars templates.
4860
* @function default
49-
* @param {object} options object containing Handlebars compiler API options
50-
* @returns {object} a Rollup plugin that precompiles Handlebars templates
61+
* @param {PluginOptions} options - plugin configuration options
62+
* @returns {RollupPlugin} - the configured plugin object
5163
*/
5264
export default function HandlebarsPrecompiler(options) {
5365
const p = new PluginImpl(options)
5466
return {
5567
name: PLUGIN_NAME,
56-
resolveId(id) { if (p.shouldEmitHelpersModule(id)) return id },
57-
load(id) { if (p.shouldEmitHelpersModule(id)) return p.helpersModule() },
58-
transform(code, id) { if (p.isTemplate(id)) return p.compile(code, id) }
68+
69+
/**
70+
* @param {string} id - import identifier to resolve
71+
* @returns {(string | undefined)} - the plugin ID if id matches it
72+
* @see https://rollupjs.org/plugin-development/#resolveid
73+
*/
74+
resolveId: function (id) {
75+
return p.shouldEmitHelpersModule(id) ? id : undefined
76+
},
77+
78+
/**
79+
* @param {string} id - import identifier to load
80+
* @returns {(string | undefined)} - the plugin helper module if id matches
81+
* @see https://rollupjs.org/plugin-development/#load
82+
*/
83+
load: function (id) {
84+
return p.shouldEmitHelpersModule(id) ? p.helpersModule() : undefined
85+
},
86+
87+
/** @type {Transform} */
88+
transform: function (code, id) {
89+
return p.isTemplate(id) ? p.compile(code, id) : undefined
90+
}
5991
}
6092
}

jsconfig.json

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"compilerOptions": {
3+
"checkJs": true,
4+
"lib": [
5+
"ES2022"
6+
],
7+
"module": "node16",
8+
"target": "es2020",
9+
"strict": true
10+
},
11+
"exclude": [
12+
"node_modules/**",
13+
"coverage*/**",
14+
"jsdoc/**"
15+
]
16+
}

lib/index.js

Lines changed: 45 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,19 +37,27 @@
3737
*/
3838

3939
import collectPartials from './partials.js'
40+
import {
41+
// eslint-disable-next-line no-unused-vars
42+
Compiled, PartialName, PartialPath, PluginOptions, SourceMap, Transform
43+
} from './types.js'
4044
import { createFilter } from '@rollup/pluginutils'
4145
import Handlebars from 'handlebars'
4246

4347
export const PLUGIN_NAME = 'handlebars-precompiler'
4448
const DEFAULT_INCLUDE = ['**/*.hbs', '**/*.handlebars', '**/*.mustache']
4549
const DEFAULT_EXCLUDE = 'node_modules/**'
4650
const DEFAULT_PARTIALS = '**/_*'
47-
const DEFAULT_PARTIAL_NAME = id => {
51+
52+
/** @type {PartialName} */
53+
const DEFAULT_PARTIAL_NAME = function (id) {
4854
return id.replace(/.*\//, '') // extract the basename
4955
.replace(/\.[^.]*$/, '') // remove the file extension, if present
5056
.replace(/^[^[:alnum:]]*/, '') // strip leading non-alphanumeric characters
5157
}
52-
const DEFAULT_PARTIAL_PATH = (partialName, importerPath) => {
58+
59+
/** @type {PartialPath} */
60+
const DEFAULT_PARTIAL_PATH = function (partialName, importerPath) {
5361
return `./_${partialName}.${importerPath.replace(/.*\./, '')}`
5462
}
5563

@@ -58,6 +66,21 @@ const HANDLEBARS_PATH = 'handlebars/lib/handlebars.runtime'
5866
const IMPORT_HANDLEBARS = `import Handlebars from '${HANDLEBARS_PATH}'`
5967
const IMPORT_HELPERS = `import Render from '${PLUGIN_ID}'`
6068

69+
/**
70+
* @callback CompilerOpts
71+
* @param {string} id - import ID of module to compile
72+
* @returns {object} - Handlebars compiler options based on id
73+
*/
74+
75+
/**
76+
* @callback AdjustSourceMap
77+
* @param {string} map - the Handlebars source map as a JSON string
78+
* @param {number} numLinesBeforeTmpl - number of empty lines to add to the
79+
* beginning of the source mappings to account for the generated code before
80+
* the precompiled template
81+
* @returns {SourceMap} - potentially modified Handlebars source map
82+
*/
83+
6184
/**
6285
* Rollup Handlebars precompiler implementation
6386
*/
@@ -67,10 +90,15 @@ export default class PluginImpl {
6790
#isPartial
6891
#partialName
6992
#partialPath
93+
/** @type {CompilerOpts} */
7094
#compilerOpts
95+
/** @type {AdjustSourceMap} */
7196
#adjustSourceMap
7297

73-
constructor(options = {}) {
98+
/**
99+
* @param {PluginOptions} options - plugin configuration options
100+
*/
101+
constructor(options = /** @type {PluginOptions} */ ({})) {
74102
this.#helpers = options.helpers || []
75103
this.#isTemplate = createFilter(
76104
options.include || DEFAULT_INCLUDE,
@@ -101,6 +129,10 @@ export default class PluginImpl {
101129
}
102130
}
103131

132+
/**
133+
* @param {string} id - import identifier
134+
* @returns {boolean} - true if id is the plugin's import identifier
135+
*/
104136
shouldEmitHelpersModule(id) { return id === PLUGIN_ID }
105137

106138
helpersModule() {
@@ -118,12 +150,17 @@ export default class PluginImpl {
118150
].join('\n')
119151
}
120152

153+
/**
154+
* @param {string} id - import identifier
155+
* @returns {boolean} - true if id matches the filter for template files
156+
*/
121157
isTemplate(id) { return this.#isTemplate(id) }
122158

159+
/** @type {Transform} */
123160
compile(code, id) {
124161
const opts = this.#compilerOpts(id)
125162
const ast = Handlebars.parse(code, opts)
126-
const compiled = Handlebars.precompile(ast, opts)
163+
const compiled = /** @type {Compiled} */ (Handlebars.precompile(ast, opts))
127164
const { code: tmpl = compiled, map: srcMap } = compiled
128165

129166
const beforeTmpl = [
@@ -143,6 +180,10 @@ export default class PluginImpl {
143180
}
144181
}
145182

183+
/**
184+
* @param {string} id - id of the partial to register
185+
* @returns {string} - Handlebars.registerPartial statement for the partial
186+
*/
146187
#partialRegistration(id) {
147188
return `Handlebars.registerPartial('${this.#partialName(id)}', RawTemplate)`
148189
}

lib/partials.js

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,18 +43,28 @@ import Handlebars from 'handlebars'
4343
* @see https://github.com/handlebars-lang/handlebars.js/blob/master/docs/compiler-api.md
4444
*/
4545
class PartialCollector extends Handlebars.Visitor {
46+
/** @type {string[]} */
4647
partials = []
4748

49+
/**
50+
* @param {hbs.AST.PartialStatement} partial - partial name to evaluate
51+
*/
4852
PartialStatement(partial) {
4953
this.collect(partial.name)
50-
return super.PartialStatement(partial)
54+
super.PartialStatement(partial)
5155
}
52-
56+
/**
57+
* @param {hbs.AST.PartialBlockStatement} partial - partial name to evaluate
58+
*/
5359
PartialBlockStatement(partial) {
5460
this.collect(partial.name)
55-
return super.PartialBlockStatement(partial)
61+
super.PartialBlockStatement(partial)
5662
}
5763

64+
/**
65+
* @param {hbs.AST.PathExpression | hbs.AST.SubExpression} n - potential
66+
* partial name to collect
67+
*/
5868
collect(n) {
5969
if (n.type === 'PathExpression' && n.original !== '@partial-block') {
6070
this.partials.push(n.original)
@@ -64,11 +74,11 @@ class PartialCollector extends Handlebars.Visitor {
6474

6575
/**
6676
* Returns the partial names parsed from a Handlebars template
77+
* @param {hbs.AST.Program} ast - abstract syntax tree for a Handlebars template
78+
* returned by Handlebars.parse()
79+
* @returns {string[]} - a list of partial names parsed from the template
6780
* @see https://handlebarsjs.com/guide/partials.html
6881
* @see https://github.com/handlebars-lang/handlebars.js/blob/master/docs/compiler-api.md
69-
* @param {object} ast - abstract syntax tree for a Handlebars template returned
70-
* by Handlebars.parse()
71-
* @returns {string[]} - a list of partial names parsed from the template
7282
*/
7383
export default function collectPartials(ast) {
7484
const collector = new PartialCollector()

0 commit comments

Comments
 (0)