@open-xchange/vite-plugin-ox-manifests 0.5.2 → 0.6.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/CHANGELOG.md +39 -0
- package/dist/index.d.ts +56 -0
- package/dist/index.js +175 -0
- package/dist/plugins/gettext.d.ts +2 -0
- package/dist/plugins/gettext.js +63 -0
- package/dist/plugins/manifests.d.ts +2 -0
- package/dist/plugins/manifests.js +132 -0
- package/dist/plugins/meta.d.ts +2 -0
- package/dist/plugins/meta.js +21 -0
- package/dist/plugins/plugin.d.ts +24 -0
- package/dist/plugins/plugin.js +6 -0
- package/dist/plugins/relative-paths.d.ts +2 -0
- package/dist/plugins/relative-paths.js +9 -0
- package/dist/plugins/serve.d.ts +2 -0
- package/dist/plugins/serve.js +66 -0
- package/dist/plugins/settings.d.ts +2 -0
- package/dist/plugins/settings.js +141 -0
- package/dist/util.d.ts +11 -0
- package/dist/util.js +77 -0
- package/package.json +24 -22
- package/.eslintrc.cjs +0 -10
- package/.gitlab-ci.yml +0 -26
- package/index.js +0 -172
- package/src/constants.js +0 -1
- package/src/plugins/gettext.js +0 -69
- package/src/plugins/manifests.js +0 -136
- package/src/plugins/meta.js +0 -13
- package/src/plugins/relative-paths.js +0 -7
- package/src/plugins/serve.js +0 -64
- package/src/plugins/settings.js +0 -147
- package/src/util.js +0 -61
- package/test/.eslintrc.cjs +0 -5
- package/test/deps/main.test.js +0 -32
- package/test/gettext-chunked/another_chunk.js +0 -4
- package/test/gettext-chunked/i18n/de_DE.po +0 -16
- package/test/gettext-chunked/i18n/en_US.po +0 -16
- package/test/gettext-chunked/main.test.js +0 -67
- package/test/gettext-chunked/manifest.json +0 -3
- package/test/gettext-chunked/register.js +0 -6
- package/test/gettext-multiple/i18n/de_DE.po +0 -22
- package/test/gettext-multiple/i18n/en_US.po +0 -22
- package/test/gettext-multiple/main.test.js +0 -71
- package/test/gettext-multiple/manifest.json +0 -3
- package/test/gettext-multiple/register.js +0 -4
- package/test/gettext-simple/i18n/de_DE.po +0 -18
- package/test/gettext-simple/i18n/en_US.po +0 -18
- package/test/gettext-simple/main.test.js +0 -61
- package/test/gettext-simple/manifest.json +0 -3
- package/test/gettext-simple/register.js +0 -3
- package/test/hmr/main.test.js +0 -121
- package/test/hmr/manifest.json +0 -3
- package/test/hmr/register.js +0 -1
- package/test/hmr/settings.js +0 -3
- package/test/html-relative-paths/index.html +0 -7
- package/test/html-relative-paths/index.js +0 -3
- package/test/html-relative-paths/main.test.js +0 -39
- package/test/html-relative-paths/other.html +0 -7
- package/test/html-relative-paths/style.css +0 -1
- package/test/manifest-dependencies/main.test.js +0 -26
- package/test/manifest-dependencies/manifest.json +0 -3
- package/test/manifest-dependencies/register.js +0 -3
- package/test/manifest-dependencies/style.less +0 -1
- package/test/manifest-dynamic-deps/main.test.js +0 -28
- package/test/manifest-dynamic-deps/manifest.json +0 -3
- package/test/manifest-dynamic-deps/register.js +0 -7
- package/test/manifest-dynamic-deps/some_dep.js +0 -5
- package/test/manifest-dynamic-deps/style.less +0 -1
- package/test/manifest-multiple/entry1.js +0 -1
- package/test/manifest-multiple/main.test.js +0 -83
- package/test/manifest-multiple/manifest.json +0 -7
- package/test/manifest-multiple/sub/entry2.js +0 -3
- package/test/manifest-multiple/sub/manifest.json +0 -4
- package/test/manifest-simple/main.test.js +0 -105
- package/test/manifest-simple/manifest.json +0 -3
- package/test/manifest-simple/register.js +0 -1
- package/test/meta/main.test.js +0 -25
- package/test/meta/manifest.json +0 -3
- package/test/meta/register.js +0 -1
- package/test/preload-helper-bundled/async.js +0 -1
- package/test/preload-helper-bundled/index.js +0 -1
- package/test/preload-helper-bundled/main.test.js +0 -32
- package/test/preload-helper-bundled/manifest.json +0 -4
- package/test/preload-helper-separate/bar.js +0 -1
- package/test/preload-helper-separate/foo.js +0 -3
- package/test/preload-helper-separate/index.js +0 -1
- package/test/preload-helper-separate/main.test.js +0 -34
- package/test/preload-helper-separate/manifest.json +0 -4
- package/test/settings-core/index.js +0 -4
- package/test/settings-core/main.test.js +0 -56
- package/test/settings-core/manifest.json +0 -4
- package/test/settings-core/no-settings.js +0 -3
- package/test/settings-core/settings.js +0 -3
- package/test/settings-external/index.js +0 -3
- package/test/settings-external/main.test.js +0 -61
- package/test/settings-external/manifest.json +0 -4
- package/test/settings-external/settings.js +0 -3
- package/test/settings-ts/index.js +0 -3
- package/test/settings-ts/main.test.js +0 -30
- package/test/settings-ts/manifest.json +0 -4
- package/test/settings-ts/settings.ts +0 -4
- package/test/settings-with-requirements/foo.js +0 -3
- package/test/settings-with-requirements/forth-settings.js +0 -3
- package/test/settings-with-requirements/index.js +0 -5
- package/test/settings-with-requirements/main.test.js +0 -95
- package/test/settings-with-requirements/manifest.json +0 -12
- package/test/settings-with-requirements/other-settings.js +0 -3
- package/test/settings-with-requirements/other.js +0 -4
- package/test/settings-with-requirements/settings.js +0 -3
- package/test/settings-with-requirements/third-settings.js +0 -3
- package/test/util/deep-merge.test.js +0 -59
- package/test/util.js +0 -16
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
## [Unreleased]
|
|
6
|
+
|
|
7
|
+
## [0.6.0] - 2023-05-16
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
- Convert source code to TypeScript [`029a4c7`](https://gitlab.open-xchange.com/frontend/vite-plugin-ox-manifests/commit/029a4c75c96483a221a54fc54589467b1a77eead)
|
|
12
|
+
|
|
13
|
+
### Removed
|
|
14
|
+
|
|
15
|
+
- Coverage files from npm package [`11fc7a8`](https://gitlab.open-xchange.com/frontend/vite-plugin-ox-manifests/commit/11fc7a8ba825f1a3bf1913b0134b57b1d1029f99)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
## [0.5.4] - 2023-03-17
|
|
19
|
+
|
|
20
|
+
### Removed
|
|
21
|
+
|
|
22
|
+
- Test files from npm package [`9d76062`](https://gitlab.open-xchange.com/frontend/vite-plugin-ox-manifests/commit/9d760623362bcbbb7e52fd551a0693cc43c61ed5)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
## [0.5.3] - 2023-03-17
|
|
26
|
+
|
|
27
|
+
### Added
|
|
28
|
+
|
|
29
|
+
- Add @open-xchange/lint [`c5ac8b6`](https://gitlab.open-xchange.com/frontend/vite-plugin-ox-manifests/commit/c5ac8b6009c8c035a2c609a2368a7ccfd422e65a)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
## [0.5.2] - 2022-12-13
|
|
33
|
+
|
|
34
|
+
- Start Changelog
|
|
35
|
+
|
|
36
|
+
[unreleased]: https://gitlab.open-xchange.com/frontend/vite-plugin-ox-manifests/compare/0.6.0...main
|
|
37
|
+
[0.6.0]: https://gitlab.open-xchange.com/frontend/vite-plugin-ox-manifests/compare/0.5.4...0.6.0
|
|
38
|
+
[0.5.4]: https://gitlab.open-xchange.com/frontend/vite-plugin-ox-manifests/compare/0.5.3...0.5.4
|
|
39
|
+
[0.5.3]: https://gitlab.open-xchange.com/frontend/vite-plugin-ox-manifests/compare/0.5.2...0.5.3
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { Plugin } from 'vite';
|
|
2
|
+
import { mergeManifests } from './util';
|
|
3
|
+
export { mergeManifests };
|
|
4
|
+
export interface OxManifest {
|
|
5
|
+
namespace: string;
|
|
6
|
+
path: string;
|
|
7
|
+
requires?: string;
|
|
8
|
+
raw?: string;
|
|
9
|
+
generator?: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Configuration options for the Vite plugin "vite-plugin-ox-manifests".
|
|
13
|
+
*/
|
|
14
|
+
export interface VitePluginOxManifestsOptions {
|
|
15
|
+
/**
|
|
16
|
+
* If set to `true`, this plugin will watch manifest changes and reload the
|
|
17
|
+
* UI. Default value is `false`.
|
|
18
|
+
*/
|
|
19
|
+
watch?: boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Convenience method to specify additional entry points for the production
|
|
22
|
+
* build. Can be specified as a glob-pattern.
|
|
23
|
+
*/
|
|
24
|
+
entryPoints?: string;
|
|
25
|
+
/**
|
|
26
|
+
* Specifies, that every path in a manifest is used as an entry-point for the
|
|
27
|
+
* production build. Default is `true`!
|
|
28
|
+
*/
|
|
29
|
+
manifestsAsEntryPoints?: boolean;
|
|
30
|
+
/**
|
|
31
|
+
* If disabled, settings will not be automatically detected and therefore
|
|
32
|
+
* have to provide a manual manifest.json. Default is `true`!
|
|
33
|
+
*/
|
|
34
|
+
autoloadSettings?: boolean;
|
|
35
|
+
/**
|
|
36
|
+
* An array of valid file-extensions for entry points. Default is
|
|
37
|
+
* `['js', 'mjs', 'ts']`.
|
|
38
|
+
*/
|
|
39
|
+
supportedEntryExtensions?: string[];
|
|
40
|
+
/**
|
|
41
|
+
* An object that will be translated into a `meta.json` file in the root
|
|
42
|
+
* directory.
|
|
43
|
+
*/
|
|
44
|
+
meta?: object | null;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Type shape of the Vite plugin "vite-plugin-ox-manifests".
|
|
48
|
+
*/
|
|
49
|
+
export interface VitePluginOxManifests extends Plugin {
|
|
50
|
+
getManifests(): Promise<OxManifest[]>;
|
|
51
|
+
}
|
|
52
|
+
export declare const PROJECT_NAME = "@open-xchange/vite-plugin-ox-manifests";
|
|
53
|
+
/**
|
|
54
|
+
* Creates a vite-plugin to include manifests in dev and production mode
|
|
55
|
+
*/
|
|
56
|
+
export default function pluginOxManifests(options?: VitePluginOxManifestsOptions): VitePluginOxManifests;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { deepMergeObject, mergeManifests } from './util';
|
|
2
|
+
import manifestsPlugin from './plugins/manifests';
|
|
3
|
+
import relativePathsPlugin from './plugins/relative-paths';
|
|
4
|
+
import settingsPlugin from './plugins/settings';
|
|
5
|
+
import servePlugin from './plugins/serve';
|
|
6
|
+
import gettextPlugin from './plugins/gettext';
|
|
7
|
+
import metaPlugin from './plugins/meta';
|
|
8
|
+
export { mergeManifests };
|
|
9
|
+
export const PROJECT_NAME = '@open-xchange/vite-plugin-ox-manifests';
|
|
10
|
+
/**
|
|
11
|
+
* Creates a vite-plugin to include manifests in dev and production mode
|
|
12
|
+
*/
|
|
13
|
+
export default function pluginOxManifests(options) {
|
|
14
|
+
const resolvedOptions = {
|
|
15
|
+
watch: false,
|
|
16
|
+
entryPoints: '',
|
|
17
|
+
manifestsAsEntryPoints: true,
|
|
18
|
+
autoloadSettings: true,
|
|
19
|
+
supportedEntryExtensions: ['js', 'mjs', 'ts'],
|
|
20
|
+
meta: null,
|
|
21
|
+
...options
|
|
22
|
+
};
|
|
23
|
+
let resolvedConfig;
|
|
24
|
+
const plugins = [
|
|
25
|
+
// add settings first
|
|
26
|
+
settingsPlugin(resolvedOptions),
|
|
27
|
+
relativePathsPlugin(resolvedOptions),
|
|
28
|
+
servePlugin(resolvedOptions),
|
|
29
|
+
gettextPlugin(resolvedOptions),
|
|
30
|
+
metaPlugin(resolvedOptions),
|
|
31
|
+
// manifest plugin last
|
|
32
|
+
manifestsPlugin(resolvedOptions)
|
|
33
|
+
];
|
|
34
|
+
const pluginManifests = {
|
|
35
|
+
name: PROJECT_NAME,
|
|
36
|
+
enforce: 'post',
|
|
37
|
+
async config(config, env) {
|
|
38
|
+
for (const plugin of plugins) {
|
|
39
|
+
plugin.pluginResolved?.(pluginManifests);
|
|
40
|
+
if (typeof plugin.config === 'function') {
|
|
41
|
+
config = (await plugin.config.call(this, config, env)) || config;
|
|
42
|
+
}
|
|
43
|
+
else if (plugin.config) {
|
|
44
|
+
throw new TypeError(`object hooks not supported, specify function callback (${plugin.name}.config)`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return config;
|
|
48
|
+
},
|
|
49
|
+
async getManifests() {
|
|
50
|
+
const manifests = [];
|
|
51
|
+
for (const plugin of plugins) {
|
|
52
|
+
if (plugin.getManifests) {
|
|
53
|
+
const result = await plugin.getManifests.call(this);
|
|
54
|
+
manifests.push(...result);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return mergeManifests(manifests);
|
|
58
|
+
},
|
|
59
|
+
async configResolved(config) {
|
|
60
|
+
resolvedConfig = config;
|
|
61
|
+
for (const plugin of plugins) {
|
|
62
|
+
if (typeof plugin.configResolved === 'function') {
|
|
63
|
+
await plugin.configResolved.call(this, config);
|
|
64
|
+
}
|
|
65
|
+
else if (plugin.configResolved) {
|
|
66
|
+
throw new TypeError(`object hooks not supported, specify function callback (${plugin.name}.configResolved)`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
async options(options) {
|
|
71
|
+
if (resolvedConfig.mode === 'production') {
|
|
72
|
+
options.preserveEntrySignatures = 'strict';
|
|
73
|
+
}
|
|
74
|
+
for (const plugin of plugins) {
|
|
75
|
+
if (typeof plugin.options === 'function') {
|
|
76
|
+
options = (await plugin.options.call(this, options)) || options;
|
|
77
|
+
}
|
|
78
|
+
else if (plugin.options) {
|
|
79
|
+
throw new TypeError(`${plugin.name}.options: object hooks not supported, specify function callback`);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return options;
|
|
83
|
+
},
|
|
84
|
+
async buildStart(options) {
|
|
85
|
+
for (const plugin of plugins) {
|
|
86
|
+
if (typeof plugin.buildStart === 'function') {
|
|
87
|
+
await plugin.buildStart.call(this, options);
|
|
88
|
+
}
|
|
89
|
+
else if (plugin.buildStart) {
|
|
90
|
+
throw new TypeError(`${plugin.name}.buildStart: object hooks not supported, specify function callback`);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
async configureServer(server) {
|
|
95
|
+
for (const plugin of plugins) {
|
|
96
|
+
if (typeof plugin.configureServer === 'function') {
|
|
97
|
+
await plugin.configureServer.call(this, server);
|
|
98
|
+
}
|
|
99
|
+
else if (plugin.configureServer) {
|
|
100
|
+
throw new TypeError(`${plugin.name}.configureServer: object hooks not supported, specify function callback`);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
async resolveId(source, importer, options) {
|
|
105
|
+
for (const plugin of plugins) {
|
|
106
|
+
if (typeof plugin.resolveId === 'function') {
|
|
107
|
+
const result = await plugin.resolveId.call(this, source, importer, options);
|
|
108
|
+
if (result !== undefined)
|
|
109
|
+
return result;
|
|
110
|
+
}
|
|
111
|
+
else if (plugin.resolveId) {
|
|
112
|
+
throw new TypeError(`${plugin.name}.resolveId: object hooks not supported, specify function callback`);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return undefined;
|
|
116
|
+
},
|
|
117
|
+
async load(id) {
|
|
118
|
+
for (const plugin of plugins) {
|
|
119
|
+
if (typeof plugin.load === 'function') {
|
|
120
|
+
const result = await plugin.load.call(this, id);
|
|
121
|
+
if (result !== undefined)
|
|
122
|
+
return result;
|
|
123
|
+
}
|
|
124
|
+
else if (plugin.load) {
|
|
125
|
+
throw new TypeError(`${plugin.name}.load: object hooks not supported, specify function callback`);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return undefined;
|
|
129
|
+
},
|
|
130
|
+
async transform(code, id) {
|
|
131
|
+
let result = {};
|
|
132
|
+
for (const plugin of plugins) {
|
|
133
|
+
if (typeof plugin.transform === 'function') {
|
|
134
|
+
// TODO check what happens if there is already meta from other plugins
|
|
135
|
+
const that = await plugin.transform.call(this, code, id);
|
|
136
|
+
if (that && typeof that === 'object') {
|
|
137
|
+
result = deepMergeObject(result, that);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
else if (plugin.transform) {
|
|
141
|
+
throw new TypeError(`${plugin.name}.transform: object hooks not supported, specify function callback`);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return Object.keys(result).length ? result : undefined;
|
|
145
|
+
},
|
|
146
|
+
async generateBundle(options, bundle, isWrite) {
|
|
147
|
+
for (const plugin of plugins) {
|
|
148
|
+
if (typeof plugin.generateBundle === 'function') {
|
|
149
|
+
await plugin.generateBundle.call(this, options, bundle, isWrite);
|
|
150
|
+
}
|
|
151
|
+
else if (plugin.generateBundle) {
|
|
152
|
+
throw new TypeError(`${plugin.name}.generateBundle: object hooks not supported, specify function callback`);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
},
|
|
156
|
+
async transformIndexHtml(src, ctx) {
|
|
157
|
+
for (const plugin of plugins) {
|
|
158
|
+
if (typeof plugin.transformIndexHtml === 'function') {
|
|
159
|
+
const result = await plugin.transformIndexHtml.call(this, src, ctx);
|
|
160
|
+
if (typeof result === 'string') {
|
|
161
|
+
src = result;
|
|
162
|
+
}
|
|
163
|
+
else if (result) {
|
|
164
|
+
throw new TypeError(`${plugin.name}.transformIndexHtml: tag descriptors not supported, return simple string`);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
else if (plugin.transformIndexHtml) {
|
|
168
|
+
throw new TypeError(`${plugin.name}.transformIndexHtml: object hooks not supported, specify function callback`);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return src;
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
return pluginManifests;
|
|
175
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { dirname, posix } from 'node:path';
|
|
2
|
+
import { readdir } from 'node:fs/promises';
|
|
3
|
+
import { normalizePath } from 'vite';
|
|
4
|
+
import { PROJECT_NAME as GETTEXT_PROJECT_NAME, parsePoFile, namespacesFrom } from '@open-xchange/rollup-plugin-po2json';
|
|
5
|
+
import { definePlugin } from './plugin';
|
|
6
|
+
import { applyInputToOptions } from '../util';
|
|
7
|
+
import { PROJECT_NAME } from '../index';
|
|
8
|
+
export default definePlugin(() => {
|
|
9
|
+
const manifests = [];
|
|
10
|
+
let resolvedConfig;
|
|
11
|
+
return {
|
|
12
|
+
name: `${PROJECT_NAME}/gettext-plugin`,
|
|
13
|
+
configResolved(config) {
|
|
14
|
+
resolvedConfig = config;
|
|
15
|
+
},
|
|
16
|
+
async options(options) {
|
|
17
|
+
const gettextPlugin = resolvedConfig.plugins.find(plugin => plugin.name === GETTEXT_PROJECT_NAME);
|
|
18
|
+
if (!gettextPlugin)
|
|
19
|
+
return;
|
|
20
|
+
const { meta: { poFiles, defaultDictionary, defaultLanguage } } = gettextPlugin;
|
|
21
|
+
const input = {};
|
|
22
|
+
// add dictionaries as entry points
|
|
23
|
+
const poDir = dirname(poFiles);
|
|
24
|
+
const files = (await readdir(poDir)).filter(f => f.indexOf(defaultLanguage) >= 0).map(f => normalizePath(`${poDir}/${f}`));
|
|
25
|
+
await Promise.all(files.map(async (file) => {
|
|
26
|
+
const po = await parsePoFile(file);
|
|
27
|
+
for (const namespace of namespacesFrom(po.items)) {
|
|
28
|
+
if (namespace !== defaultDictionary) {
|
|
29
|
+
input[namespace] = `gettext?dictionary=${namespace}`;
|
|
30
|
+
manifests.push({ namespace: 'i18n', path: namespace });
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}));
|
|
34
|
+
input[defaultDictionary] = 'gettext';
|
|
35
|
+
manifests.push({ namespace: 'i18n', path: defaultDictionary });
|
|
36
|
+
if (resolvedConfig.mode === 'production') {
|
|
37
|
+
applyInputToOptions(input, options);
|
|
38
|
+
}
|
|
39
|
+
return options;
|
|
40
|
+
},
|
|
41
|
+
getManifests() {
|
|
42
|
+
if (resolvedConfig.mode === 'production')
|
|
43
|
+
return manifests;
|
|
44
|
+
return manifests.map(manifest => ({ ...manifest, raw: posix.join(resolvedConfig.base, `/@id/${manifest.path}.js`) }));
|
|
45
|
+
},
|
|
46
|
+
generateBundle(_options, bundle) {
|
|
47
|
+
for (const file in bundle) {
|
|
48
|
+
const chunk = bundle[file];
|
|
49
|
+
if (chunk.type !== 'chunk' || !chunk.facadeModuleId)
|
|
50
|
+
continue;
|
|
51
|
+
const modules = Object.keys(chunk.modules);
|
|
52
|
+
if (modules.length === 0)
|
|
53
|
+
modules.push(chunk.facadeModuleId);
|
|
54
|
+
for (const id of modules) {
|
|
55
|
+
const meta = this.getModuleInfo(id)?.meta;
|
|
56
|
+
if (meta.gettext?.dictionary === true) {
|
|
57
|
+
(meta.manifests ??= []).push({ namespace: 'i18n', path: '' });
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
});
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import fastGlob from 'fast-glob';
|
|
4
|
+
import { definePlugin } from './plugin';
|
|
5
|
+
import { applyInputToOptions, basepath, joinUrlPaths, stringifyJSON } from '../util';
|
|
6
|
+
import { PROJECT_NAME } from '../index';
|
|
7
|
+
async function getManifestEntryFile(basePath, extensions) {
|
|
8
|
+
if (path.extname(basePath)) {
|
|
9
|
+
try {
|
|
10
|
+
await fs.promises.stat(basePath);
|
|
11
|
+
return path.parse(basePath);
|
|
12
|
+
}
|
|
13
|
+
catch (e) {
|
|
14
|
+
throw new Error(`Cannot find file "${basePath}"`);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
for (const ext of extensions) {
|
|
18
|
+
try {
|
|
19
|
+
await fs.promises.stat(`${basePath}.${ext}`);
|
|
20
|
+
return path.parse(`${basePath}.${ext}`);
|
|
21
|
+
}
|
|
22
|
+
catch { }
|
|
23
|
+
}
|
|
24
|
+
throw new Error(`Cannot find file "${basePath}"`);
|
|
25
|
+
}
|
|
26
|
+
export default definePlugin(({ supportedEntryExtensions, entryPoints, manifestsAsEntryPoints }) => {
|
|
27
|
+
const manifestModules = new Map();
|
|
28
|
+
let resolvedConfig;
|
|
29
|
+
let inputOptions;
|
|
30
|
+
let viteManifestPlugin;
|
|
31
|
+
async function buildViteManifests(options, bundle) {
|
|
32
|
+
return new Promise((resolve, reject) => {
|
|
33
|
+
// local async context to satisfy type checker expecting void callback for promise constructor
|
|
34
|
+
(async function () {
|
|
35
|
+
if (typeof viteManifestPlugin.buildStart !== 'function' || typeof viteManifestPlugin.generateBundle !== 'function') {
|
|
36
|
+
throw new Error(`${PROJECT_NAME}: missing required callbacks in plugin 'vite:manifest'`);
|
|
37
|
+
}
|
|
38
|
+
// fake `PluginContext` to be passed to Vite's manifest plugin, needed to extract the asset data
|
|
39
|
+
const context = {
|
|
40
|
+
emitFile(file) {
|
|
41
|
+
if (file.type === 'asset' && typeof file.source === 'string') {
|
|
42
|
+
resolve(JSON.parse(file.source));
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
reject(new Error(`${PROJECT_NAME}: received unexpected manifest data from plugin 'vite:manifest'`));
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
getFileName: () => 'Not implemented'
|
|
49
|
+
};
|
|
50
|
+
// manually invoke plugin hooks (in a local async context to satisfy type checker expecting sync promise callbacks)
|
|
51
|
+
await viteManifestPlugin.buildStart.call(context, inputOptions);
|
|
52
|
+
await viteManifestPlugin.generateBundle.call(context, options, bundle, false);
|
|
53
|
+
})().catch(reject);
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
return {
|
|
57
|
+
name: `${PROJECT_NAME}/manifests-plugin`,
|
|
58
|
+
config(config) {
|
|
59
|
+
(config.build ??= {}).manifest = true;
|
|
60
|
+
},
|
|
61
|
+
configResolved(config) {
|
|
62
|
+
resolvedConfig = config;
|
|
63
|
+
if (resolvedConfig.mode !== 'production')
|
|
64
|
+
return;
|
|
65
|
+
// extract the vite:manifest plugin from the list
|
|
66
|
+
const index = config.plugins.findIndex(plugin => plugin.name === 'vite:manifest');
|
|
67
|
+
viteManifestPlugin = config.plugins.splice(index, 1)[0];
|
|
68
|
+
},
|
|
69
|
+
async options(options) {
|
|
70
|
+
const input = {};
|
|
71
|
+
if (entryPoints) {
|
|
72
|
+
const entryPointFiles = await fastGlob(entryPoints, { absolute: true });
|
|
73
|
+
for (const entry of entryPointFiles) {
|
|
74
|
+
const baseLength = resolvedConfig.root.length + 1;
|
|
75
|
+
const extLength = path.extname(entry).length;
|
|
76
|
+
input[entry.substring(baseLength, entry.length - extLength)] = entry;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
// 1. load all json files
|
|
80
|
+
const root = resolvedConfig.root;
|
|
81
|
+
const entries = await fastGlob(`${root.replace(/\\/g, '/')}/**/manifest.json`);
|
|
82
|
+
// 2. find every file that is referenced by that json file
|
|
83
|
+
for (const entry of entries) {
|
|
84
|
+
const rawData = await fs.promises.readFile(entry, 'utf-8');
|
|
85
|
+
const json = JSON.parse(rawData);
|
|
86
|
+
const manifests = Array.isArray(json) ? json : [json];
|
|
87
|
+
const manifestDir = path.dirname(entry).substring(root.length + 1);
|
|
88
|
+
await Promise.all(manifests.map(async ({ path: srcManifestEntryPath = joinUrlPaths(manifestDir, 'register'), ...rest }) => {
|
|
89
|
+
const { dir, base } = await getManifestEntryFile(joinUrlPaths(root, srcManifestEntryPath), supportedEntryExtensions);
|
|
90
|
+
const manifestEntryPath = joinUrlPaths(dir, base);
|
|
91
|
+
if (manifestsAsEntryPoints) {
|
|
92
|
+
input[basepath(joinUrlPaths(path.dirname(srcManifestEntryPath), base))] = manifestEntryPath;
|
|
93
|
+
}
|
|
94
|
+
// 3. put the file content except path into the manifest modules
|
|
95
|
+
const modules = manifestModules.get(manifestEntryPath);
|
|
96
|
+
manifestModules.set(manifestEntryPath, (modules || []).concat({ path: '', ...rest }));
|
|
97
|
+
}));
|
|
98
|
+
}
|
|
99
|
+
if (resolvedConfig.mode === 'production') {
|
|
100
|
+
applyInputToOptions(input, options);
|
|
101
|
+
}
|
|
102
|
+
return options;
|
|
103
|
+
},
|
|
104
|
+
buildStart(options) {
|
|
105
|
+
inputOptions = options;
|
|
106
|
+
},
|
|
107
|
+
// specific call to collect all manifests
|
|
108
|
+
getManifests() {
|
|
109
|
+
return Array.from(manifestModules, ([path, manifests]) => manifests.map(m => ({ ...m, path: basepath(path.slice(resolvedConfig.root.length + 1)) }))).flat(1);
|
|
110
|
+
},
|
|
111
|
+
transform(_code, id) {
|
|
112
|
+
if (resolvedConfig.mode !== 'production')
|
|
113
|
+
return;
|
|
114
|
+
const manifests = manifestModules.get(id);
|
|
115
|
+
return manifests ? { meta: { manifests } } : undefined;
|
|
116
|
+
},
|
|
117
|
+
async generateBundle(options, bundle) {
|
|
118
|
+
const viteManifests = await buildViteManifests(options, bundle);
|
|
119
|
+
for (const entry in viteManifests) {
|
|
120
|
+
const chunk = bundle[viteManifests[entry].file];
|
|
121
|
+
if (chunk.type === 'chunk' && chunk.facadeModuleId) {
|
|
122
|
+
viteManifests[entry].meta = this.getModuleInfo(chunk.facadeModuleId)?.meta;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
this.emitFile({
|
|
126
|
+
fileName: 'manifest.json',
|
|
127
|
+
type: 'asset',
|
|
128
|
+
source: stringifyJSON(resolvedConfig, viteManifests)
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { definePlugin } from './plugin';
|
|
2
|
+
import { stringifyJSON } from '../util';
|
|
3
|
+
import { PROJECT_NAME } from '../index';
|
|
4
|
+
export default definePlugin(({ meta }) => {
|
|
5
|
+
let resolvedConfig;
|
|
6
|
+
return {
|
|
7
|
+
name: `${PROJECT_NAME}/meta-plugin`,
|
|
8
|
+
configResolved(config) {
|
|
9
|
+
resolvedConfig = config;
|
|
10
|
+
},
|
|
11
|
+
generateBundle() {
|
|
12
|
+
if (meta && Object.keys(meta).length) {
|
|
13
|
+
this.emitFile({
|
|
14
|
+
fileName: 'meta.json',
|
|
15
|
+
type: 'asset',
|
|
16
|
+
source: stringifyJSON(resolvedConfig, meta)
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
});
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { Plugin } from 'vite';
|
|
2
|
+
import type { GettextPluginModuleMeta } from '@open-xchange/rollup-plugin-po2json';
|
|
3
|
+
import type { OxManifest, VitePluginOxManifests, VitePluginOxManifestsOptions } from '../index';
|
|
4
|
+
/**
|
|
5
|
+
* Type shape of an internal manifests implementation plugin.
|
|
6
|
+
*/
|
|
7
|
+
export interface VitePluginOxManifestsPlugin extends Plugin {
|
|
8
|
+
pluginResolved?(plugin: VitePluginOxManifests): void;
|
|
9
|
+
getManifests?(): OxManifest[] | Promise<OxManifest[]>;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Type shape of internal module metadata used by the manifests plugin.
|
|
13
|
+
*/
|
|
14
|
+
export interface VitePluginOxManifestsModuleMeta extends GettextPluginModuleMeta {
|
|
15
|
+
manifests?: OxManifest[];
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Plugin generator function.
|
|
19
|
+
*/
|
|
20
|
+
export type VitePluginOxManifestsPluginFn = (options: Required<VitePluginOxManifestsOptions>) => VitePluginOxManifestsPlugin;
|
|
21
|
+
/**
|
|
22
|
+
* Helper for type safety. Inspired by Vite's `defineConfig`.
|
|
23
|
+
*/
|
|
24
|
+
export declare function definePlugin(fn: VitePluginOxManifestsPluginFn): VitePluginOxManifestsPluginFn;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { definePlugin } from './plugin';
|
|
2
|
+
import { PROJECT_NAME } from '../index';
|
|
3
|
+
export default definePlugin(options => {
|
|
4
|
+
if ('transformAbsolutePaths' in options)
|
|
5
|
+
console.warn("transformAbsolutePaths is no longer used. Use `base: './'` with vite 3");
|
|
6
|
+
return {
|
|
7
|
+
name: `${PROJECT_NAME}/relative-paths-plugin`
|
|
8
|
+
};
|
|
9
|
+
});
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import path, { posix } from 'node:path';
|
|
2
|
+
import parseurl from 'parseurl';
|
|
3
|
+
import chokidar from 'chokidar';
|
|
4
|
+
import { definePlugin } from './plugin';
|
|
5
|
+
import { PROJECT_NAME } from '../index';
|
|
6
|
+
function sendJSON(res, data) {
|
|
7
|
+
res.statusCode = 200;
|
|
8
|
+
res.setHeader('Content-Type', 'application/json');
|
|
9
|
+
res.write(JSON.stringify(data));
|
|
10
|
+
return res.end();
|
|
11
|
+
}
|
|
12
|
+
export default definePlugin(({ watch }) => {
|
|
13
|
+
let resolvedConfig;
|
|
14
|
+
let resolvedPlugin;
|
|
15
|
+
let manifestRegex;
|
|
16
|
+
let depsRegex;
|
|
17
|
+
return {
|
|
18
|
+
name: `${PROJECT_NAME}/serve-plugin`,
|
|
19
|
+
pluginResolved(plugin) {
|
|
20
|
+
resolvedPlugin = plugin;
|
|
21
|
+
},
|
|
22
|
+
configResolved(config) {
|
|
23
|
+
resolvedConfig = config;
|
|
24
|
+
manifestRegex = new RegExp(`^${config.base}(api/manifest.json|manifests)$`);
|
|
25
|
+
depsRegex = new RegExp(`^${config.base}(api/deps.json|dependencies)$`);
|
|
26
|
+
},
|
|
27
|
+
configureServer({ ws, middlewares, moduleGraph }) {
|
|
28
|
+
function addTimestamp(manifests) {
|
|
29
|
+
for (const manifest of manifests) {
|
|
30
|
+
// TODO what about other file types?
|
|
31
|
+
const id = `${resolvedConfig.root}/${manifest.path}.js`;
|
|
32
|
+
const moduleNode = moduleGraph.idToModuleMap.get(id);
|
|
33
|
+
const hmrTS = moduleNode?.lastHMRTimestamp;
|
|
34
|
+
if (hmrTS)
|
|
35
|
+
manifest.raw = posix.join(resolvedConfig.base, `/${manifest.path}.js?t=${hmrTS}`);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
middlewares.use((req, res, next) => {
|
|
39
|
+
if (req.method !== 'GET')
|
|
40
|
+
return next();
|
|
41
|
+
const url = parseurl(req);
|
|
42
|
+
if (!url?.path)
|
|
43
|
+
return next();
|
|
44
|
+
if (manifestRegex.test(url.path)) {
|
|
45
|
+
resolvedPlugin.getManifests().then(manifests => {
|
|
46
|
+
addTimestamp(manifests);
|
|
47
|
+
sendJSON(res, manifests);
|
|
48
|
+
}, next);
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
if (depsRegex.test(url.path)) {
|
|
52
|
+
return sendJSON(res, {});
|
|
53
|
+
}
|
|
54
|
+
next();
|
|
55
|
+
});
|
|
56
|
+
if (watch) {
|
|
57
|
+
const watcher = chokidar.watch(path.join(resolvedConfig.root, '**/manifest.json'))
|
|
58
|
+
.on('ready', () => {
|
|
59
|
+
watcher.on('all', path => {
|
|
60
|
+
ws.send({ type: 'full-reload', path });
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
});
|