@rollup-extras/plugin-html 1.0.0
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/LICENSE +21 -0
- package/README.md +208 -0
- package/dist/index.cjs +334 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.mjs +326 -0
- package/dist/types.d.ts +34 -0
- package/package.json +77 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2021 Konstantin Shutkin
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
# Plugin Html
|
|
2
|
+
|
|
3
|
+
Rollup plugin to inject assets names into html template.
|
|
4
|
+
|
|
5
|
+
Points:
|
|
6
|
+
|
|
7
|
+
- Inject file names with hashes
|
|
8
|
+
- Watch on a template file and trigger rebuild if it is changed
|
|
9
|
+
- Provides minimalistic template by default, so you are ready to start without configuration
|
|
10
|
+
- Support mutliple rollup configs (will trigger only when last output generated)
|
|
11
|
+
- Extensible through API so you can plug in something for html processing or generate new types of html elements
|
|
12
|
+
|
|
13
|
+
Uses [`@niceties/logger`](https://github.com/kshutkin/niceties/blob/main/logger/README.md) to log messages, can be configured through `@niceties/logger` API.
|
|
14
|
+
|
|
15
|
+
[Changlelog](./CHANGELOG.md)
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
Using npm:
|
|
20
|
+
```
|
|
21
|
+
npm install --save-dev @rollup-extras/plugin-html
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Usage
|
|
25
|
+
|
|
26
|
+
```javascript
|
|
27
|
+
import html from '@rollup-extras/plugin-html';
|
|
28
|
+
|
|
29
|
+
export default {
|
|
30
|
+
input: 'src/index.js',
|
|
31
|
+
|
|
32
|
+
output: {
|
|
33
|
+
format: 'es',
|
|
34
|
+
dir: 'dest'
|
|
35
|
+
},
|
|
36
|
+
|
|
37
|
+
plugins: [html()],
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Providing options
|
|
42
|
+
|
|
43
|
+
Just pass options to the plugin function. Returned object is the plugin instance which can be passed to rollup.
|
|
44
|
+
|
|
45
|
+
```javascript
|
|
46
|
+
html({option: value, option2: value2})
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
For additional plugin instances (in case of multiple configs) please use: `firstInstance.api.addInstance()`
|
|
50
|
+
|
|
51
|
+
## Options
|
|
52
|
+
|
|
53
|
+
### pluginName
|
|
54
|
+
|
|
55
|
+
Optional, string.
|
|
56
|
+
|
|
57
|
+
For debugging purposes, so many instances of a plugin can be differenciated in debugging output.
|
|
58
|
+
|
|
59
|
+
### outputFile
|
|
60
|
+
|
|
61
|
+
Optional, string, default: `'index.html'`.
|
|
62
|
+
|
|
63
|
+
Use to override output file name. If file name with the same name exits in pipeline it will be removed or overwritten in the process but its content by default will be used as an input for this plugin. In the following example file emitted by copy plugin will be used as an input for this plugin:
|
|
64
|
+
|
|
65
|
+
```javascript
|
|
66
|
+
import copy from '@rollup-extras/plugin-copy';
|
|
67
|
+
import html from '@rollup-extras/plugin-html';
|
|
68
|
+
|
|
69
|
+
export default {
|
|
70
|
+
input: 'src/index.js',
|
|
71
|
+
|
|
72
|
+
output: {
|
|
73
|
+
format: 'es',
|
|
74
|
+
dir: 'dest'
|
|
75
|
+
},
|
|
76
|
+
|
|
77
|
+
plugins: [copy('src/index.html'), html()],
|
|
78
|
+
};
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### template
|
|
82
|
+
|
|
83
|
+
Optional, string, default: `'<!DOCTYPE html><html><head></head><body></body></html>'`.
|
|
84
|
+
|
|
85
|
+
Default template string or template file name. If it is a template string it should contain `</head>` and `</body>` substrings to be useful by default template factory.
|
|
86
|
+
|
|
87
|
+
### watch
|
|
88
|
+
|
|
89
|
+
Optional, boolean, default: true.
|
|
90
|
+
|
|
91
|
+
If plugin found a template file name this option defines if plugin need to watch it or not.
|
|
92
|
+
|
|
93
|
+
### emitFile
|
|
94
|
+
|
|
95
|
+
Optional, boolean, default: true.
|
|
96
|
+
|
|
97
|
+
Defines if plugin should use this.emitFile or should just write it to disk. Option can be ignored in setup with mutliple rollup configs.
|
|
98
|
+
|
|
99
|
+
### useEmittedTemplate
|
|
100
|
+
|
|
101
|
+
Optional, boolean, default: true.
|
|
102
|
+
|
|
103
|
+
Defines what plugin does if it finds a file with expected file name in bundle. By default it will be used as a template. Template provided through template option should be of higher priority to the plugin.
|
|
104
|
+
|
|
105
|
+
### conditionalLoading
|
|
106
|
+
|
|
107
|
+
Optional, boolean, default: undefined.
|
|
108
|
+
|
|
109
|
+
Defines if plugin adds `nomodule` attribute for non modular js chunks. By default it is done only if we have `es` outputs in one of the bundles processed by plugin. Also plugin values `iife` outputs higher than `umd` and if we have both filters out `umd` ones. This can be changes only by providing custom `assetFactory`.
|
|
110
|
+
|
|
111
|
+
### injectIntoHead
|
|
112
|
+
|
|
113
|
+
Optional, RegExp | function | boolean, default: `(fileName: string) => fileName.endsWith(cssExtention)`.
|
|
114
|
+
|
|
115
|
+
Option to customize what assets should be injected into head element of a template.
|
|
116
|
+
|
|
117
|
+
### ignore
|
|
118
|
+
|
|
119
|
+
Optional, RegExp | function | boolean, default: false.
|
|
120
|
+
|
|
121
|
+
Option to customize what assets should be ignored in process.
|
|
122
|
+
|
|
123
|
+
### verbose
|
|
124
|
+
|
|
125
|
+
Optional, boolean, default: false.
|
|
126
|
+
|
|
127
|
+
Option to print more debug information into console (with default appender).
|
|
128
|
+
|
|
129
|
+
### useWriteBundle
|
|
130
|
+
|
|
131
|
+
Optional, boolean, default: false.
|
|
132
|
+
|
|
133
|
+
Option to use `writeBundle` hook instead of `generateBundle`.
|
|
134
|
+
|
|
135
|
+
### assetsFactory
|
|
136
|
+
|
|
137
|
+
Optional, function (please check type in configuration section).
|
|
138
|
+
|
|
139
|
+
To process additional types of assets / enchance default behavior. If known asset processed by factory (it returned an object, string or promise) plugin skips default processing for this asset.
|
|
140
|
+
|
|
141
|
+
Example (adds integrity attribute to a css file):
|
|
142
|
+
```javascript
|
|
143
|
+
import copy from '@rollup-extras/plugin-copy';
|
|
144
|
+
import html from '@rollup-extras/plugin-html';
|
|
145
|
+
import crypto from 'crypto';
|
|
146
|
+
|
|
147
|
+
export default {
|
|
148
|
+
input: 'src/index.js',
|
|
149
|
+
|
|
150
|
+
output: {
|
|
151
|
+
format: 'es',
|
|
152
|
+
dir: 'dest'
|
|
153
|
+
},
|
|
154
|
+
|
|
155
|
+
plugins: [copy('src/test.css'), html({
|
|
156
|
+
assetsFactory: (fileName, content) => {
|
|
157
|
+
if (fileName.endsWith('.css')) {
|
|
158
|
+
const data = crypto
|
|
159
|
+
.createHash('sha384')
|
|
160
|
+
.update(content);
|
|
161
|
+
return `<link rel="stylesheet" href="${fileName}" integrity="sha384-${data.digest('base64')}" type="text/css">`;
|
|
162
|
+
}
|
|
163
|
+
return undefined;
|
|
164
|
+
}
|
|
165
|
+
})],
|
|
166
|
+
};
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### templateFactory
|
|
170
|
+
|
|
171
|
+
Optional, function (please check type in configuration section).
|
|
172
|
+
|
|
173
|
+
Use to customize template with external libraries.
|
|
174
|
+
|
|
175
|
+
Example (pretty print html):
|
|
176
|
+
|
|
177
|
+
```javascript
|
|
178
|
+
import html from '@rollup-extras/plugin-html';
|
|
179
|
+
import sb from 'simply-beautiful';
|
|
180
|
+
|
|
181
|
+
export default {
|
|
182
|
+
input: 'src/index.js',
|
|
183
|
+
|
|
184
|
+
output: {
|
|
185
|
+
format: 'es',
|
|
186
|
+
dir: 'dest'
|
|
187
|
+
},
|
|
188
|
+
|
|
189
|
+
plugins: [html({
|
|
190
|
+
templateFactory: (template, assets, defaultFactory) => sb.html(defaultFactory(template, assets))
|
|
191
|
+
})],
|
|
192
|
+
};
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
## Configuration
|
|
196
|
+
|
|
197
|
+
[Definition of config typings in typescript](./src/types.ts)
|
|
198
|
+
|
|
199
|
+
## Prior Art
|
|
200
|
+
|
|
201
|
+
- https://github.com/haifeng2013/rollup-plugin-bundle-html
|
|
202
|
+
- https://github.com/rollup/plugins/tree/master/packages/html
|
|
203
|
+
- https://github.com/modernweb-dev/web/tree/master/packages/rollup-plugin-html
|
|
204
|
+
- https://github.com/posthtml/posthtml#rollup
|
|
205
|
+
|
|
206
|
+
# License
|
|
207
|
+
|
|
208
|
+
[MIT](./LICENSE)
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var fs = require('fs/promises');
|
|
4
|
+
var oldStyleFs = require('fs');
|
|
5
|
+
var path = require('path');
|
|
6
|
+
var logger = require('@niceties/logger');
|
|
7
|
+
|
|
8
|
+
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
9
|
+
|
|
10
|
+
var fs__default = /*#__PURE__*/_interopDefaultLegacy(fs);
|
|
11
|
+
var oldStyleFs__default = /*#__PURE__*/_interopDefaultLegacy(oldStyleFs);
|
|
12
|
+
var path__default = /*#__PURE__*/_interopDefaultLegacy(path);
|
|
13
|
+
|
|
14
|
+
const defaultTemplate = '<!DOCTYPE html><html><head></head><body></body></html>';
|
|
15
|
+
const headClosingElement = '</head>';
|
|
16
|
+
const bodyClosingElement = '</body>';
|
|
17
|
+
const cssExtention = '.css';
|
|
18
|
+
function index (options = {}) {
|
|
19
|
+
const { pluginName, outputFile, template, watch, emitFile, conditionalLoading, logger, useWriteBundle, logLevel, useEmittedTemplate, injectIntoHead, ignore, assetsFactory, templateFactory } = normalizeOptions(options), hasCustomTemplateFactory = 'templateFactory' in options;
|
|
20
|
+
let templateString = defaultTemplate, hasTemplateFile = false;
|
|
21
|
+
if (template) {
|
|
22
|
+
if (isUsableByDefaultTemplateFactory(template)) {
|
|
23
|
+
templateString = template;
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
if (watch) {
|
|
27
|
+
try {
|
|
28
|
+
// using sync fs call because we need to return plugin object immidiately
|
|
29
|
+
useNewTemplate(oldStyleFs__default["default"].readFileSync(template, { encoding: 'utf8' }));
|
|
30
|
+
hasTemplateFile = true;
|
|
31
|
+
}
|
|
32
|
+
catch (e) {
|
|
33
|
+
handleTemplateReadError(e);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
templateString = new Promise(resolve => {
|
|
38
|
+
fs__default["default"].readFile(template, { encoding: 'utf8' })
|
|
39
|
+
.then((newTemplateString) => {
|
|
40
|
+
if (!isUsableByDefaultTemplateFactory(newTemplateString) && !hasCustomTemplateFactory) {
|
|
41
|
+
warnAboutUsingDefaultTemplate();
|
|
42
|
+
resolve(defaultTemplate);
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
resolve(newTemplateString);
|
|
46
|
+
}
|
|
47
|
+
})
|
|
48
|
+
.catch(handleTemplateReadError);
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
let remainingOutputsCount = 0, configsCount = 0, initialDir = '', fileNameInInitialDir, assets = freshAssets();
|
|
54
|
+
const configs = new Set(), finalHook = useWriteBundle ? 'writeBundle' : 'generateBundle', processedFiles = new Set();
|
|
55
|
+
const instance = {
|
|
56
|
+
name: pluginName,
|
|
57
|
+
renderStart: (options) => {
|
|
58
|
+
logger('started collecting information', logLevel);
|
|
59
|
+
initialDir = options.dir || '';
|
|
60
|
+
fileNameInInitialDir = path__default["default"].join(initialDir, outputFile);
|
|
61
|
+
return renderStart();
|
|
62
|
+
},
|
|
63
|
+
[finalHook]: generateBundle,
|
|
64
|
+
api: { addInstance }
|
|
65
|
+
};
|
|
66
|
+
if (hasTemplateFile && watch) {
|
|
67
|
+
instance.buildStart = async function () {
|
|
68
|
+
try {
|
|
69
|
+
useNewTemplate(await fs__default["default"].readFile(template, { encoding: 'utf8' }));
|
|
70
|
+
}
|
|
71
|
+
catch (e) {
|
|
72
|
+
handleTemplateReadError(e);
|
|
73
|
+
}
|
|
74
|
+
await buildStart.apply(this);
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
return instance;
|
|
78
|
+
function freshAssets() {
|
|
79
|
+
return {
|
|
80
|
+
asset: [],
|
|
81
|
+
es: [],
|
|
82
|
+
iife: [],
|
|
83
|
+
umd: []
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
function useNewTemplate(newTemplateString) {
|
|
87
|
+
if (!isUsableByDefaultTemplateFactory(newTemplateString) && !hasCustomTemplateFactory) {
|
|
88
|
+
warnAboutUsingDefaultTemplate();
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
templateString = newTemplateString;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
function handleTemplateReadError(e) {
|
|
95
|
+
var _a;
|
|
96
|
+
if (((_a = e) === null || _a === void 0 ? void 0 : _a.code) === 'ENOENT') {
|
|
97
|
+
logger('template nor a file or string', 2 /* warn */, e);
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
logger(`error reading template\n${e}`, 2 /* warn */, e);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
function warnAboutUsingDefaultTemplate() {
|
|
104
|
+
logger('template is unusable by default template factory, using default one', 2 /* warn */);
|
|
105
|
+
}
|
|
106
|
+
function addInstance() {
|
|
107
|
+
const configId = ++configsCount;
|
|
108
|
+
configs.add(configId);
|
|
109
|
+
const instance = {
|
|
110
|
+
name: `${pluginName}#${configId}`,
|
|
111
|
+
renderStart: () => {
|
|
112
|
+
configs.delete(configId);
|
|
113
|
+
return renderStart();
|
|
114
|
+
},
|
|
115
|
+
[finalHook]: generateBundle
|
|
116
|
+
};
|
|
117
|
+
if (hasTemplateFile && watch) {
|
|
118
|
+
instance.buildStart = buildStart;
|
|
119
|
+
}
|
|
120
|
+
return instance;
|
|
121
|
+
}
|
|
122
|
+
async function buildStart() {
|
|
123
|
+
this.addWatchFile('src/index.html');
|
|
124
|
+
}
|
|
125
|
+
function renderStart() {
|
|
126
|
+
++remainingOutputsCount;
|
|
127
|
+
}
|
|
128
|
+
async function generateBundle(options, bundle) {
|
|
129
|
+
--remainingOutputsCount;
|
|
130
|
+
await getAssets(options, bundle);
|
|
131
|
+
const statistics = Object.keys(assets)
|
|
132
|
+
.map(key => ({ key, count: assets[key].length }))
|
|
133
|
+
.filter(item => item.count)
|
|
134
|
+
.map(({ key, count }) => `${key}: ${count}`)
|
|
135
|
+
.join(', ');
|
|
136
|
+
logger(`assets collected: [${statistics}], remaining: ${remainingOutputsCount} outputs, ${configs.size} configs`, logLevel);
|
|
137
|
+
const dir = options.dir || '', fileName = path__default["default"].relative(dir, fileNameInInitialDir);
|
|
138
|
+
if (fileName in bundle) {
|
|
139
|
+
if (useEmittedTemplate) {
|
|
140
|
+
logger(`using exiting emitted ${fileName} as an input for out templateFactory`, logLevel);
|
|
141
|
+
const source = bundle[fileName].type === 'asset' ? bundle[fileName].source.toString() : bundle[fileName].code;
|
|
142
|
+
useNewTemplate(source);
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
logger(`removing exiting emitted ${fileName}`, logLevel);
|
|
146
|
+
}
|
|
147
|
+
if (configs.size === 0 && emitFile && !useWriteBundle) {
|
|
148
|
+
delete bundle[fileName];
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
if (configs.size === 0 && remainingOutputsCount === 0) {
|
|
152
|
+
logger.start('generating html', logLevel);
|
|
153
|
+
try {
|
|
154
|
+
const depromisifiedTemplateString = await templateString, source = await templateFactory(depromisifiedTemplateString, assets, defaultTemplateFactory);
|
|
155
|
+
if (!emitFile || fileName.startsWith('..') || useWriteBundle) {
|
|
156
|
+
if (emitFile && emitFile !== 'auto') {
|
|
157
|
+
logger('cannot emitFile because it is outside of current output.dir, using writeFile instead', logLevel);
|
|
158
|
+
}
|
|
159
|
+
await fs__default["default"].mkdir(path__default["default"].dirname(fileNameInInitialDir), { recursive: true });
|
|
160
|
+
await fs__default["default"].writeFile(fileNameInInitialDir, source);
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
this.emitFile({
|
|
164
|
+
type: 'asset',
|
|
165
|
+
fileName,
|
|
166
|
+
source
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
// reset assets and configs for next iteration
|
|
170
|
+
assets = freshAssets();
|
|
171
|
+
for (let i = configsCount; i > 0; --i) {
|
|
172
|
+
configs.add(i);
|
|
173
|
+
}
|
|
174
|
+
processedFiles.clear();
|
|
175
|
+
logger.finish('html file generated');
|
|
176
|
+
}
|
|
177
|
+
catch (e) {
|
|
178
|
+
logger(`error generating html file\n${e}`, 3 /* error */, e);
|
|
179
|
+
logger.finish('html generation failed', 3 /* error */);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
async function getAssets(options, bundle) {
|
|
184
|
+
const dir = options.dir || '';
|
|
185
|
+
for (const fileName of Object.keys(bundle)) {
|
|
186
|
+
const relativeToRootAssetPath = path__default["default"].join(dir, fileName);
|
|
187
|
+
if (ignore(relativeToRootAssetPath) || processedFiles.has(relativeToRootAssetPath)) {
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
processedFiles.add(relativeToRootAssetPath);
|
|
191
|
+
if (bundle[fileName].type == 'asset') {
|
|
192
|
+
if (await useAssetFactory(fileName, relativeToRootAssetPath, bundle[fileName].source, 'asset')) {
|
|
193
|
+
continue;
|
|
194
|
+
}
|
|
195
|
+
if (fileName.endsWith(cssExtention)) {
|
|
196
|
+
const assetPath = path__default["default"].relative(initialDir, relativeToRootAssetPath);
|
|
197
|
+
assets.asset.push({
|
|
198
|
+
html: getLinkElement(assetPath),
|
|
199
|
+
head: injectIntoHead(relativeToRootAssetPath),
|
|
200
|
+
type: 'asset'
|
|
201
|
+
});
|
|
202
|
+
continue;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
else if (bundle[fileName].type == 'chunk') {
|
|
206
|
+
const chunk = bundle[fileName];
|
|
207
|
+
if (chunk.isEntry) {
|
|
208
|
+
if (await useAssetFactory(fileName, relativeToRootAssetPath, chunk.code, options.format)) {
|
|
209
|
+
continue;
|
|
210
|
+
}
|
|
211
|
+
if (options.format === 'es' || options.format === 'iife' || options.format === 'umd') {
|
|
212
|
+
const assetPath = path__default["default"].relative(initialDir, relativeToRootAssetPath);
|
|
213
|
+
assets[options.format].push({
|
|
214
|
+
html: (assets) => {
|
|
215
|
+
let useConditionalLoading = conditionalLoading;
|
|
216
|
+
useConditionalLoading !== null && useConditionalLoading !== void 0 ? useConditionalLoading : (useConditionalLoading = !!((assets.iife.length
|
|
217
|
+
|| assets.umd.length)
|
|
218
|
+
&& assets.es.length));
|
|
219
|
+
return (options.format === 'iife' || options.format === 'umd') ? getNonModuleScriptElement(assetPath, useConditionalLoading) : getModuleScriptElement(assetPath);
|
|
220
|
+
},
|
|
221
|
+
head: injectIntoHead(relativeToRootAssetPath),
|
|
222
|
+
type: 'asset'
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
async function useAssetFactory(fileName, relativeToRootAssetPath, source, format) {
|
|
230
|
+
if (assetsFactory) {
|
|
231
|
+
try {
|
|
232
|
+
let asset = await assetsFactory(fileName, source, format);
|
|
233
|
+
if (asset) {
|
|
234
|
+
if (typeof asset === 'string') {
|
|
235
|
+
asset = {
|
|
236
|
+
html: asset,
|
|
237
|
+
head: injectIntoHead(relativeToRootAssetPath),
|
|
238
|
+
type: format
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
if (!assets[asset.type]) {
|
|
242
|
+
assets[asset.type] = [];
|
|
243
|
+
}
|
|
244
|
+
assets[asset.type].push(asset);
|
|
245
|
+
return true;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
catch (e) {
|
|
249
|
+
logger(`exception in assetFactory:${e}`, 2 /* warn */, e);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
return false;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
function isUsableByDefaultTemplateFactory(template) {
|
|
256
|
+
return template.indexOf(headClosingElement) >= 0 && template.indexOf(bodyClosingElement) >= 0;
|
|
257
|
+
}
|
|
258
|
+
function getLinkElement(fileName) {
|
|
259
|
+
return `<link rel="stylesheet" href="${fileName}" type="text/css">`;
|
|
260
|
+
}
|
|
261
|
+
function getNonModuleScriptElement(fileName, conditionalLoading) {
|
|
262
|
+
return `<script src="${fileName}" type="text/javascript"${conditionalLoading ? ' nomodule' : ''}></script>`;
|
|
263
|
+
}
|
|
264
|
+
function getModuleScriptElement(fileName) {
|
|
265
|
+
return `<script src="${fileName}" type="module"></script>`;
|
|
266
|
+
}
|
|
267
|
+
function normalizeOptions(userOptions) {
|
|
268
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
269
|
+
const options = {
|
|
270
|
+
pluginName: (_a = userOptions.pluginName) !== null && _a !== void 0 ? _a : '@rollup-extras/plugin-html',
|
|
271
|
+
template: userOptions.template,
|
|
272
|
+
outputFile: (_b = userOptions.outputFile) !== null && _b !== void 0 ? _b : 'index.html',
|
|
273
|
+
watch: (_c = userOptions.watch) !== null && _c !== void 0 ? _c : true,
|
|
274
|
+
useWriteBundle: (_d = userOptions.useWriteBundle) !== null && _d !== void 0 ? _d : false,
|
|
275
|
+
useEmittedTemplate: (_e = userOptions.useEmittedTemplate) !== null && _e !== void 0 ? _e : !('template' in userOptions),
|
|
276
|
+
emitFile: (_f = userOptions.emitFile) !== null && _f !== void 0 ? _f : 'auto',
|
|
277
|
+
logLevel: userOptions.verbose ? 1 /* info */ : 0 /* verbose */,
|
|
278
|
+
conditionalLoading: userOptions.conditionalLoading,
|
|
279
|
+
injectIntoHead: (fileName) => fileName.endsWith(cssExtention),
|
|
280
|
+
ignore: toAssetPredicate(false),
|
|
281
|
+
assetsFactory: userOptions.assetsFactory,
|
|
282
|
+
templateFactory: (_g = userOptions.templateFactory) !== null && _g !== void 0 ? _g : ((template, assets, defaultTemplateFactory) => defaultTemplateFactory(template, assets)),
|
|
283
|
+
}, logger$1 = options.logger = logger.createLogger(options.pluginName), optionIgnoredText = 'option ignored because it is not a function, RegExp or boolean';
|
|
284
|
+
if (userOptions.injectIntoHead != null) {
|
|
285
|
+
const predicate = toAssetPredicate(userOptions.injectIntoHead);
|
|
286
|
+
if (predicate) {
|
|
287
|
+
options.injectIntoHead = predicate;
|
|
288
|
+
}
|
|
289
|
+
else {
|
|
290
|
+
logger$1(`injectIntoHead ${optionIgnoredText}`, 2 /* warn */);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
if (userOptions.ignore != null) {
|
|
294
|
+
const predicate = toAssetPredicate(userOptions.ignore);
|
|
295
|
+
if (predicate) {
|
|
296
|
+
options.ignore = predicate;
|
|
297
|
+
}
|
|
298
|
+
else {
|
|
299
|
+
logger$1(`ignore ${optionIgnoredText}`, 2 /* warn */);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
return options;
|
|
303
|
+
}
|
|
304
|
+
function toAssetPredicate(sourceOption) {
|
|
305
|
+
if (typeof sourceOption === 'boolean') {
|
|
306
|
+
return () => sourceOption;
|
|
307
|
+
}
|
|
308
|
+
else if (typeof sourceOption === 'function') {
|
|
309
|
+
return sourceOption;
|
|
310
|
+
}
|
|
311
|
+
else if (sourceOption instanceof RegExp) {
|
|
312
|
+
return (fileName) => sourceOption.test(fileName);
|
|
313
|
+
}
|
|
314
|
+
return undefined;
|
|
315
|
+
}
|
|
316
|
+
function defaultTemplateFactory(template, assets) {
|
|
317
|
+
const assetsAsArray = assets.asset
|
|
318
|
+
.concat((assets.iife.length ? assets.iife : assets.umd))
|
|
319
|
+
.concat(assets.es)
|
|
320
|
+
.map((asset) => {
|
|
321
|
+
if (typeof asset.html === 'function') {
|
|
322
|
+
asset.html = asset.html(assets);
|
|
323
|
+
}
|
|
324
|
+
return asset;
|
|
325
|
+
});
|
|
326
|
+
const headElements = assetsAsArray.filter(asset => asset.head).map(asset => asset.html);
|
|
327
|
+
const bodyElements = assetsAsArray.filter(asset => !asset.head).map(asset => asset.html);
|
|
328
|
+
const headIndex = template.toLowerCase().indexOf(headClosingElement);
|
|
329
|
+
template = `${template.substring(0, headIndex)}${headElements.join('')}${template.substring(headIndex)}`;
|
|
330
|
+
const bodyIndex = template.toLowerCase().indexOf(bodyClosingElement);
|
|
331
|
+
return `${template.substring(0, bodyIndex)}${bodyElements.join('')}${template.substring(bodyIndex)}`;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
module.exports = index;
|
package/dist/index.d.ts
ADDED
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
import fs from 'fs/promises';
|
|
2
|
+
import oldStyleFs from 'fs';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import { createLogger } from '@niceties/logger';
|
|
5
|
+
|
|
6
|
+
const defaultTemplate = '<!DOCTYPE html><html><head></head><body></body></html>';
|
|
7
|
+
const headClosingElement = '</head>';
|
|
8
|
+
const bodyClosingElement = '</body>';
|
|
9
|
+
const cssExtention = '.css';
|
|
10
|
+
function index (options = {}) {
|
|
11
|
+
const { pluginName, outputFile, template, watch, emitFile, conditionalLoading, logger, useWriteBundle, logLevel, useEmittedTemplate, injectIntoHead, ignore, assetsFactory, templateFactory } = normalizeOptions(options), hasCustomTemplateFactory = 'templateFactory' in options;
|
|
12
|
+
let templateString = defaultTemplate, hasTemplateFile = false;
|
|
13
|
+
if (template) {
|
|
14
|
+
if (isUsableByDefaultTemplateFactory(template)) {
|
|
15
|
+
templateString = template;
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
if (watch) {
|
|
19
|
+
try {
|
|
20
|
+
// using sync fs call because we need to return plugin object immidiately
|
|
21
|
+
useNewTemplate(oldStyleFs.readFileSync(template, { encoding: 'utf8' }));
|
|
22
|
+
hasTemplateFile = true;
|
|
23
|
+
}
|
|
24
|
+
catch (e) {
|
|
25
|
+
handleTemplateReadError(e);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
templateString = new Promise(resolve => {
|
|
30
|
+
fs.readFile(template, { encoding: 'utf8' })
|
|
31
|
+
.then((newTemplateString) => {
|
|
32
|
+
if (!isUsableByDefaultTemplateFactory(newTemplateString) && !hasCustomTemplateFactory) {
|
|
33
|
+
warnAboutUsingDefaultTemplate();
|
|
34
|
+
resolve(defaultTemplate);
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
resolve(newTemplateString);
|
|
38
|
+
}
|
|
39
|
+
})
|
|
40
|
+
.catch(handleTemplateReadError);
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
let remainingOutputsCount = 0, configsCount = 0, initialDir = '', fileNameInInitialDir, assets = freshAssets();
|
|
46
|
+
const configs = new Set(), finalHook = useWriteBundle ? 'writeBundle' : 'generateBundle', processedFiles = new Set();
|
|
47
|
+
const instance = {
|
|
48
|
+
name: pluginName,
|
|
49
|
+
renderStart: (options) => {
|
|
50
|
+
logger('started collecting information', logLevel);
|
|
51
|
+
initialDir = options.dir || '';
|
|
52
|
+
fileNameInInitialDir = path.join(initialDir, outputFile);
|
|
53
|
+
return renderStart();
|
|
54
|
+
},
|
|
55
|
+
[finalHook]: generateBundle,
|
|
56
|
+
api: { addInstance }
|
|
57
|
+
};
|
|
58
|
+
if (hasTemplateFile && watch) {
|
|
59
|
+
instance.buildStart = async function () {
|
|
60
|
+
try {
|
|
61
|
+
useNewTemplate(await fs.readFile(template, { encoding: 'utf8' }));
|
|
62
|
+
}
|
|
63
|
+
catch (e) {
|
|
64
|
+
handleTemplateReadError(e);
|
|
65
|
+
}
|
|
66
|
+
await buildStart.apply(this);
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
return instance;
|
|
70
|
+
function freshAssets() {
|
|
71
|
+
return {
|
|
72
|
+
asset: [],
|
|
73
|
+
es: [],
|
|
74
|
+
iife: [],
|
|
75
|
+
umd: []
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
function useNewTemplate(newTemplateString) {
|
|
79
|
+
if (!isUsableByDefaultTemplateFactory(newTemplateString) && !hasCustomTemplateFactory) {
|
|
80
|
+
warnAboutUsingDefaultTemplate();
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
templateString = newTemplateString;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
function handleTemplateReadError(e) {
|
|
87
|
+
var _a;
|
|
88
|
+
if (((_a = e) === null || _a === void 0 ? void 0 : _a.code) === 'ENOENT') {
|
|
89
|
+
logger('template nor a file or string', 2 /* warn */, e);
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
logger(`error reading template\n${e}`, 2 /* warn */, e);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
function warnAboutUsingDefaultTemplate() {
|
|
96
|
+
logger('template is unusable by default template factory, using default one', 2 /* warn */);
|
|
97
|
+
}
|
|
98
|
+
function addInstance() {
|
|
99
|
+
const configId = ++configsCount;
|
|
100
|
+
configs.add(configId);
|
|
101
|
+
const instance = {
|
|
102
|
+
name: `${pluginName}#${configId}`,
|
|
103
|
+
renderStart: () => {
|
|
104
|
+
configs.delete(configId);
|
|
105
|
+
return renderStart();
|
|
106
|
+
},
|
|
107
|
+
[finalHook]: generateBundle
|
|
108
|
+
};
|
|
109
|
+
if (hasTemplateFile && watch) {
|
|
110
|
+
instance.buildStart = buildStart;
|
|
111
|
+
}
|
|
112
|
+
return instance;
|
|
113
|
+
}
|
|
114
|
+
async function buildStart() {
|
|
115
|
+
this.addWatchFile('src/index.html');
|
|
116
|
+
}
|
|
117
|
+
function renderStart() {
|
|
118
|
+
++remainingOutputsCount;
|
|
119
|
+
}
|
|
120
|
+
async function generateBundle(options, bundle) {
|
|
121
|
+
--remainingOutputsCount;
|
|
122
|
+
await getAssets(options, bundle);
|
|
123
|
+
const statistics = Object.keys(assets)
|
|
124
|
+
.map(key => ({ key, count: assets[key].length }))
|
|
125
|
+
.filter(item => item.count)
|
|
126
|
+
.map(({ key, count }) => `${key}: ${count}`)
|
|
127
|
+
.join(', ');
|
|
128
|
+
logger(`assets collected: [${statistics}], remaining: ${remainingOutputsCount} outputs, ${configs.size} configs`, logLevel);
|
|
129
|
+
const dir = options.dir || '', fileName = path.relative(dir, fileNameInInitialDir);
|
|
130
|
+
if (fileName in bundle) {
|
|
131
|
+
if (useEmittedTemplate) {
|
|
132
|
+
logger(`using exiting emitted ${fileName} as an input for out templateFactory`, logLevel);
|
|
133
|
+
const source = bundle[fileName].type === 'asset' ? bundle[fileName].source.toString() : bundle[fileName].code;
|
|
134
|
+
useNewTemplate(source);
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
logger(`removing exiting emitted ${fileName}`, logLevel);
|
|
138
|
+
}
|
|
139
|
+
if (configs.size === 0 && emitFile && !useWriteBundle) {
|
|
140
|
+
delete bundle[fileName];
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
if (configs.size === 0 && remainingOutputsCount === 0) {
|
|
144
|
+
logger.start('generating html', logLevel);
|
|
145
|
+
try {
|
|
146
|
+
const depromisifiedTemplateString = await templateString, source = await templateFactory(depromisifiedTemplateString, assets, defaultTemplateFactory);
|
|
147
|
+
if (!emitFile || fileName.startsWith('..') || useWriteBundle) {
|
|
148
|
+
if (emitFile && emitFile !== 'auto') {
|
|
149
|
+
logger('cannot emitFile because it is outside of current output.dir, using writeFile instead', logLevel);
|
|
150
|
+
}
|
|
151
|
+
await fs.mkdir(path.dirname(fileNameInInitialDir), { recursive: true });
|
|
152
|
+
await fs.writeFile(fileNameInInitialDir, source);
|
|
153
|
+
}
|
|
154
|
+
else {
|
|
155
|
+
this.emitFile({
|
|
156
|
+
type: 'asset',
|
|
157
|
+
fileName,
|
|
158
|
+
source
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
// reset assets and configs for next iteration
|
|
162
|
+
assets = freshAssets();
|
|
163
|
+
for (let i = configsCount; i > 0; --i) {
|
|
164
|
+
configs.add(i);
|
|
165
|
+
}
|
|
166
|
+
processedFiles.clear();
|
|
167
|
+
logger.finish('html file generated');
|
|
168
|
+
}
|
|
169
|
+
catch (e) {
|
|
170
|
+
logger(`error generating html file\n${e}`, 3 /* error */, e);
|
|
171
|
+
logger.finish('html generation failed', 3 /* error */);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
async function getAssets(options, bundle) {
|
|
176
|
+
const dir = options.dir || '';
|
|
177
|
+
for (const fileName of Object.keys(bundle)) {
|
|
178
|
+
const relativeToRootAssetPath = path.join(dir, fileName);
|
|
179
|
+
if (ignore(relativeToRootAssetPath) || processedFiles.has(relativeToRootAssetPath)) {
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
processedFiles.add(relativeToRootAssetPath);
|
|
183
|
+
if (bundle[fileName].type == 'asset') {
|
|
184
|
+
if (await useAssetFactory(fileName, relativeToRootAssetPath, bundle[fileName].source, 'asset')) {
|
|
185
|
+
continue;
|
|
186
|
+
}
|
|
187
|
+
if (fileName.endsWith(cssExtention)) {
|
|
188
|
+
const assetPath = path.relative(initialDir, relativeToRootAssetPath);
|
|
189
|
+
assets.asset.push({
|
|
190
|
+
html: getLinkElement(assetPath),
|
|
191
|
+
head: injectIntoHead(relativeToRootAssetPath),
|
|
192
|
+
type: 'asset'
|
|
193
|
+
});
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
else if (bundle[fileName].type == 'chunk') {
|
|
198
|
+
const chunk = bundle[fileName];
|
|
199
|
+
if (chunk.isEntry) {
|
|
200
|
+
if (await useAssetFactory(fileName, relativeToRootAssetPath, chunk.code, options.format)) {
|
|
201
|
+
continue;
|
|
202
|
+
}
|
|
203
|
+
if (options.format === 'es' || options.format === 'iife' || options.format === 'umd') {
|
|
204
|
+
const assetPath = path.relative(initialDir, relativeToRootAssetPath);
|
|
205
|
+
assets[options.format].push({
|
|
206
|
+
html: (assets) => {
|
|
207
|
+
let useConditionalLoading = conditionalLoading;
|
|
208
|
+
useConditionalLoading !== null && useConditionalLoading !== void 0 ? useConditionalLoading : (useConditionalLoading = !!((assets.iife.length
|
|
209
|
+
|| assets.umd.length)
|
|
210
|
+
&& assets.es.length));
|
|
211
|
+
return (options.format === 'iife' || options.format === 'umd') ? getNonModuleScriptElement(assetPath, useConditionalLoading) : getModuleScriptElement(assetPath);
|
|
212
|
+
},
|
|
213
|
+
head: injectIntoHead(relativeToRootAssetPath),
|
|
214
|
+
type: 'asset'
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
async function useAssetFactory(fileName, relativeToRootAssetPath, source, format) {
|
|
222
|
+
if (assetsFactory) {
|
|
223
|
+
try {
|
|
224
|
+
let asset = await assetsFactory(fileName, source, format);
|
|
225
|
+
if (asset) {
|
|
226
|
+
if (typeof asset === 'string') {
|
|
227
|
+
asset = {
|
|
228
|
+
html: asset,
|
|
229
|
+
head: injectIntoHead(relativeToRootAssetPath),
|
|
230
|
+
type: format
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
if (!assets[asset.type]) {
|
|
234
|
+
assets[asset.type] = [];
|
|
235
|
+
}
|
|
236
|
+
assets[asset.type].push(asset);
|
|
237
|
+
return true;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
catch (e) {
|
|
241
|
+
logger(`exception in assetFactory:${e}`, 2 /* warn */, e);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
return false;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
function isUsableByDefaultTemplateFactory(template) {
|
|
248
|
+
return template.indexOf(headClosingElement) >= 0 && template.indexOf(bodyClosingElement) >= 0;
|
|
249
|
+
}
|
|
250
|
+
function getLinkElement(fileName) {
|
|
251
|
+
return `<link rel="stylesheet" href="${fileName}" type="text/css">`;
|
|
252
|
+
}
|
|
253
|
+
function getNonModuleScriptElement(fileName, conditionalLoading) {
|
|
254
|
+
return `<script src="${fileName}" type="text/javascript"${conditionalLoading ? ' nomodule' : ''}></script>`;
|
|
255
|
+
}
|
|
256
|
+
function getModuleScriptElement(fileName) {
|
|
257
|
+
return `<script src="${fileName}" type="module"></script>`;
|
|
258
|
+
}
|
|
259
|
+
function normalizeOptions(userOptions) {
|
|
260
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
261
|
+
const options = {
|
|
262
|
+
pluginName: (_a = userOptions.pluginName) !== null && _a !== void 0 ? _a : '@rollup-extras/plugin-html',
|
|
263
|
+
template: userOptions.template,
|
|
264
|
+
outputFile: (_b = userOptions.outputFile) !== null && _b !== void 0 ? _b : 'index.html',
|
|
265
|
+
watch: (_c = userOptions.watch) !== null && _c !== void 0 ? _c : true,
|
|
266
|
+
useWriteBundle: (_d = userOptions.useWriteBundle) !== null && _d !== void 0 ? _d : false,
|
|
267
|
+
useEmittedTemplate: (_e = userOptions.useEmittedTemplate) !== null && _e !== void 0 ? _e : !('template' in userOptions),
|
|
268
|
+
emitFile: (_f = userOptions.emitFile) !== null && _f !== void 0 ? _f : 'auto',
|
|
269
|
+
logLevel: userOptions.verbose ? 1 /* info */ : 0 /* verbose */,
|
|
270
|
+
conditionalLoading: userOptions.conditionalLoading,
|
|
271
|
+
injectIntoHead: (fileName) => fileName.endsWith(cssExtention),
|
|
272
|
+
ignore: toAssetPredicate(false),
|
|
273
|
+
assetsFactory: userOptions.assetsFactory,
|
|
274
|
+
templateFactory: (_g = userOptions.templateFactory) !== null && _g !== void 0 ? _g : ((template, assets, defaultTemplateFactory) => defaultTemplateFactory(template, assets)),
|
|
275
|
+
}, logger = options.logger = createLogger(options.pluginName), optionIgnoredText = 'option ignored because it is not a function, RegExp or boolean';
|
|
276
|
+
if (userOptions.injectIntoHead != null) {
|
|
277
|
+
const predicate = toAssetPredicate(userOptions.injectIntoHead);
|
|
278
|
+
if (predicate) {
|
|
279
|
+
options.injectIntoHead = predicate;
|
|
280
|
+
}
|
|
281
|
+
else {
|
|
282
|
+
logger(`injectIntoHead ${optionIgnoredText}`, 2 /* warn */);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
if (userOptions.ignore != null) {
|
|
286
|
+
const predicate = toAssetPredicate(userOptions.ignore);
|
|
287
|
+
if (predicate) {
|
|
288
|
+
options.ignore = predicate;
|
|
289
|
+
}
|
|
290
|
+
else {
|
|
291
|
+
logger(`ignore ${optionIgnoredText}`, 2 /* warn */);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
return options;
|
|
295
|
+
}
|
|
296
|
+
function toAssetPredicate(sourceOption) {
|
|
297
|
+
if (typeof sourceOption === 'boolean') {
|
|
298
|
+
return () => sourceOption;
|
|
299
|
+
}
|
|
300
|
+
else if (typeof sourceOption === 'function') {
|
|
301
|
+
return sourceOption;
|
|
302
|
+
}
|
|
303
|
+
else if (sourceOption instanceof RegExp) {
|
|
304
|
+
return (fileName) => sourceOption.test(fileName);
|
|
305
|
+
}
|
|
306
|
+
return undefined;
|
|
307
|
+
}
|
|
308
|
+
function defaultTemplateFactory(template, assets) {
|
|
309
|
+
const assetsAsArray = assets.asset
|
|
310
|
+
.concat((assets.iife.length ? assets.iife : assets.umd))
|
|
311
|
+
.concat(assets.es)
|
|
312
|
+
.map((asset) => {
|
|
313
|
+
if (typeof asset.html === 'function') {
|
|
314
|
+
asset.html = asset.html(assets);
|
|
315
|
+
}
|
|
316
|
+
return asset;
|
|
317
|
+
});
|
|
318
|
+
const headElements = assetsAsArray.filter(asset => asset.head).map(asset => asset.html);
|
|
319
|
+
const bodyElements = assetsAsArray.filter(asset => !asset.head).map(asset => asset.html);
|
|
320
|
+
const headIndex = template.toLowerCase().indexOf(headClosingElement);
|
|
321
|
+
template = `${template.substring(0, headIndex)}${headElements.join('')}${template.substring(headIndex)}`;
|
|
322
|
+
const bodyIndex = template.toLowerCase().indexOf(bodyClosingElement);
|
|
323
|
+
return `${template.substring(0, bodyIndex)}${bodyElements.join('')}${template.substring(bodyIndex)}`;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
export { index as default };
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { InternalModuleFormat } from 'rollup';
|
|
2
|
+
export declare type AssetType = 'asset' | InternalModuleFormat;
|
|
3
|
+
export declare type Assets = {
|
|
4
|
+
[key in AssetType]?: AssetDescriptor[];
|
|
5
|
+
};
|
|
6
|
+
export declare type AssetPredicate = (fileName: string) => boolean;
|
|
7
|
+
export declare type AssetDescriptor = {
|
|
8
|
+
html: string | ((assets: Assets, context?: unknown) => string | unknown);
|
|
9
|
+
head: boolean;
|
|
10
|
+
type: 'asset' | InternalModuleFormat;
|
|
11
|
+
};
|
|
12
|
+
export declare type SimpleAssetDescriptor = {
|
|
13
|
+
html: string;
|
|
14
|
+
head: boolean;
|
|
15
|
+
type: 'asset' | InternalModuleFormat;
|
|
16
|
+
};
|
|
17
|
+
export declare type AssetFactory = (fileName: string, content: string | Uint8Array, type: 'asset' | InternalModuleFormat) => AssetDescriptor | string | undefined | Promise<AssetDescriptor | string | undefined>;
|
|
18
|
+
export declare type DefaultTemplateFactory = (initialTemplate: string, assets: Assets) => string;
|
|
19
|
+
export declare type TemplateFactory = (initialTemplate: string, assets: Assets, defaultTemplateFactory: DefaultTemplateFactory) => string | Uint8Array | Promise<string | Uint8Array>;
|
|
20
|
+
export declare type HtmlPluginOptions = {
|
|
21
|
+
pluginName?: string;
|
|
22
|
+
outputFile?: string;
|
|
23
|
+
template?: string;
|
|
24
|
+
watch?: boolean;
|
|
25
|
+
emitFile?: boolean;
|
|
26
|
+
verbose?: boolean;
|
|
27
|
+
useWriteBundle?: boolean;
|
|
28
|
+
useEmittedTemplate?: boolean;
|
|
29
|
+
conditionalLoading?: boolean;
|
|
30
|
+
injectIntoHead?: boolean | AssetPredicate | RegExp;
|
|
31
|
+
ignore?: boolean | AssetPredicate | RegExp;
|
|
32
|
+
assetsFactory?: AssetFactory;
|
|
33
|
+
templateFactory?: TemplateFactory;
|
|
34
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "1.0.0",
|
|
3
|
+
"license": "MIT",
|
|
4
|
+
"name": "@rollup-extras/plugin-html",
|
|
5
|
+
"author": {
|
|
6
|
+
"name": "Konstantin Shutkin"
|
|
7
|
+
},
|
|
8
|
+
"funding": "https://www.donationalerts.com/r/excitingcode",
|
|
9
|
+
"type": "module",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"require": "./dist/index.cjs",
|
|
13
|
+
"default": "./dist/index.mjs"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"main": "./dist/index.cjs",
|
|
17
|
+
"module": "./dist/index.mjs",
|
|
18
|
+
"typings": "./dist/index.d.ts",
|
|
19
|
+
"files": [
|
|
20
|
+
"dist"
|
|
21
|
+
],
|
|
22
|
+
"engines": {
|
|
23
|
+
"node": ">=15"
|
|
24
|
+
},
|
|
25
|
+
"repository": {
|
|
26
|
+
"type": "git",
|
|
27
|
+
"url": "git+https://github.com/kshutkin/rollup-extras.git"
|
|
28
|
+
},
|
|
29
|
+
"bugs": {
|
|
30
|
+
"url": "https://github.com/kshutkin/rollup-extras/issues"
|
|
31
|
+
},
|
|
32
|
+
"homepage": "https://github.com/kshutkin/rollup-extras/plugin-clean/README.md",
|
|
33
|
+
"keywords": [
|
|
34
|
+
"rollup-plugin"
|
|
35
|
+
],
|
|
36
|
+
"scripts": {
|
|
37
|
+
"build": "rimraf ./dist && rollup -c",
|
|
38
|
+
"dev": "rimraf ./dist && rollup -c -w",
|
|
39
|
+
"test": "jest --collectCoverage",
|
|
40
|
+
"lint": "eslint ./src",
|
|
41
|
+
"semantic-release": "npx semantic-release"
|
|
42
|
+
},
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"@rollup/plugin-commonjs": "21.0.1",
|
|
45
|
+
"@rollup/plugin-node-resolve": "13.0.6",
|
|
46
|
+
"@semantic-release/changelog": "6.0.1",
|
|
47
|
+
"@semantic-release/commit-analyzer": "9.0.1",
|
|
48
|
+
"@semantic-release/git": "10.0.1",
|
|
49
|
+
"@semantic-release/npm": "8.0.2",
|
|
50
|
+
"@semantic-release/release-notes-generator": "10.0.2",
|
|
51
|
+
"@types/jest": "27.0.2",
|
|
52
|
+
"@typescript-eslint/eslint-plugin": "5.3.0",
|
|
53
|
+
"@typescript-eslint/parser": "5.3.0",
|
|
54
|
+
"conventional-changelog-angular": "5.0.13",
|
|
55
|
+
"eslint": "8.1.0",
|
|
56
|
+
"jest": "27.3.1",
|
|
57
|
+
"lodash": "4.17.21",
|
|
58
|
+
"rimraf": "3.0.2",
|
|
59
|
+
"is-builtin-module": "3.1.0",
|
|
60
|
+
"rollup": "2.60.1",
|
|
61
|
+
"rollup-plugin-terser": "7.0.2",
|
|
62
|
+
"rollup-plugin-typescript2": "0.31.1",
|
|
63
|
+
"rollup-plugin-preprocess": "0.0.4",
|
|
64
|
+
"semantic-release": "18.0.0",
|
|
65
|
+
"semantic-release-monorepo": "7.0.5",
|
|
66
|
+
"ts-jest": "27.0.7",
|
|
67
|
+
"typescript": "4.4.x",
|
|
68
|
+
"update-monorepo-package-json": "0.1.6"
|
|
69
|
+
},
|
|
70
|
+
"peerDependencies": {
|
|
71
|
+
"rollup": "^2.60.2"
|
|
72
|
+
},
|
|
73
|
+
"dependencies": {
|
|
74
|
+
"@niceties/logger": "^1.0.4"
|
|
75
|
+
},
|
|
76
|
+
"readme": "ERROR: No README data found!"
|
|
77
|
+
}
|