@kopflos-cms/vite 0.2.2 → 0.3.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 CHANGED
@@ -1,5 +1,18 @@
1
1
  # @kopflos-cms/vite
2
2
 
3
+ ## 0.3.0
4
+
5
+ ### Minor Changes
6
+
7
+ - c1a1f48: Plugin configuration: `root`/`entrypoints`/`outDir` options moved to `build`, which can be single object or an array. If absent, nothing will be built. This way multiple builds can be configured.
8
+ - c1a1f48: `@kopflos-cms/vite/template.js` now does not require a previous step but loads a file provided in argument. The file path will be resolved against the correct root/build path depending on running `dev`/prod` environment
9
+
10
+ ### Patch Changes
11
+
12
+ - c1a1f48: Plugin authors can now derive from `VitePlugin` to provide build configurations
13
+ - c1a1f48: Build: `entrypoints` will be resolved against the `root` option of its respective build configuration
14
+ - c1a1f48: Updated `@zazuko/env` to v3
15
+
3
16
  ## 0.2.2
4
17
 
5
18
  ### Patch Changes
package/index.d.ts CHANGED
@@ -2,34 +2,39 @@ import type { Kopflos, KopflosEnvironment, KopflosPlugin } from '@kopflos-cms/co
2
2
  import express from 'express';
3
3
  import type { InlineConfig, ViteDevServer } from 'vite';
4
4
  export { defineConfig } from 'vite';
5
- export interface Options {
6
- configPath?: string;
7
- config?: InlineConfig;
8
- root?: string;
5
+ export interface BuildConfiguration {
6
+ root: string;
7
+ entrypoints: string[];
9
8
  outDir?: string;
10
- entrypoints?: string[];
11
- }
12
- interface VitePlugin extends KopflosPlugin {
13
- viteDevServer?: ViteDevServer;
14
9
  }
15
10
  declare module '@kopflos-cms/core' {
16
- interface PluginConfig {
17
- '@kopflos-cms/vite'?: Options;
18
- }
19
11
  interface Plugins {
20
- '@kopflos-cms/vite': VitePlugin;
12
+ '@kopflos-cms/vite': DefaultPlugin;
21
13
  }
22
14
  }
23
- export default class implements VitePlugin {
24
- private readonly options;
25
- readonly name = "@kopflos-cms/vite";
26
- private readonly rootDir;
27
- private readonly buildDir;
28
- private readonly outDir;
29
- private _viteDevServer?;
30
- constructor(options: Options);
31
- get viteDevServer(): ViteDevServer | undefined;
32
- onStart({ env }: Kopflos): Promise<void> | void;
33
- beforeMiddleware(host: express.Router, { env }: Kopflos): Promise<void>;
34
- build(env: KopflosEnvironment): Promise<void>;
15
+ export declare abstract class VitePlugin implements KopflosPlugin {
16
+ readonly name: string;
17
+ protected readonly buildConfigurations: Array<BuildConfiguration>;
18
+ private readonly log;
19
+ private _viteDevServer;
20
+ protected constructor(name: string, buildConfigurations: Array<BuildConfiguration>);
21
+ protected getDefaultPlugin(plugins: readonly KopflosPlugin[]): DefaultPlugin;
22
+ protected getViteDevServer(env: KopflosEnvironment, vitePlugin: DefaultPlugin, options: BuildConfiguration): Promise<ViteDevServer>;
23
+ private createConfig;
24
+ private resolveOutDir;
25
+ beforeMiddleware(host: express.Router, { env, plugins }: Kopflos): Promise<void>;
26
+ build(env: KopflosEnvironment, plugins: readonly KopflosPlugin[]): Promise<void>;
27
+ }
28
+ interface DefaultPluginOptions {
29
+ build?: Array<BuildConfiguration> | BuildConfiguration;
30
+ configPath?: string;
31
+ config?: InlineConfig;
32
+ }
33
+ export default class DefaultPlugin extends VitePlugin {
34
+ readonly config: InlineConfig | undefined;
35
+ readonly configPath: string | undefined;
36
+ readonly buildConfiguration?: BuildConfiguration;
37
+ constructor({ build, config, configPath }: DefaultPluginOptions);
38
+ protected getDefaultPlugin(): this;
39
+ getDefaultViteDevServer(env: KopflosEnvironment): Promise<ViteDevServer>;
35
40
  }
package/index.js CHANGED
@@ -1,57 +1,96 @@
1
1
  import { resolve } from 'node:path';
2
2
  import express from 'express';
3
- import { build } from 'vite';
4
- import { createViteServer } from './lib/server.js';
3
+ import { createServer, build } from 'vite';
4
+ import { createLogger } from '@kopflos-cms/logger';
5
5
  import { prepareConfig } from './lib/config.js';
6
- import { log } from './lib/log.js';
7
6
  export { defineConfig } from 'vite';
8
- export default class {
9
- constructor(options) {
10
- this.options = options;
11
- this.name = '@kopflos-cms/vite';
12
- this.outDir = options.outDir || 'dist';
13
- this.rootDir = options.root || '';
14
- this.buildDir = this.outDir;
15
- }
16
- get viteDevServer() {
17
- return this._viteDevServer;
18
- }
19
- onStart({ env }) {
20
- const viteVars = {
21
- basePath: resolve(env.kopflos.basePath, env.kopflos.config.mode === 'development' ? this.rootDir : this.buildDir),
22
- };
23
- log.info('Variables', viteVars);
24
- env.kopflos.variables.VITE = Object.freeze(viteVars);
25
- }
26
- async beforeMiddleware(host, { env }) {
27
- if (env.kopflos.config.mode === 'development') {
28
- log.info('Development UI mode. Creating Vite server...');
29
- const configPath = this.options.configPath
30
- ? resolve(env.kopflos.basePath, this.options.configPath)
31
- : this.options.configPath;
32
- this._viteDevServer = await createViteServer({
33
- ...this.options,
34
- configPath,
35
- });
36
- host.use(this._viteDevServer.middlewares);
7
+ export class VitePlugin {
8
+ constructor(name, buildConfigurations) {
9
+ this.name = name;
10
+ this.buildConfigurations = buildConfigurations;
11
+ this._viteDevServer = new WeakMap();
12
+ this.log = createLogger(this.name.replace(/^@kopflos-cms\//, ''));
13
+ }
14
+ getDefaultPlugin(plugins) {
15
+ const defaultPlugin = plugins.find(plugin => plugin instanceof DefaultPlugin);
16
+ if (!defaultPlugin) {
17
+ throw new Error('No default plugin found. Please add @kopflos-cms/vite to your plugins list');
37
18
  }
38
- else {
39
- const buildDir = resolve(env.kopflos.basePath, this.buildDir);
40
- log.info('Serving UI from build directory');
41
- log.debug('Build directory:', buildDir);
42
- host.use(express.static(buildDir));
19
+ return defaultPlugin;
20
+ }
21
+ async getViteDevServer(env, vitePlugin, options) {
22
+ if (!this._viteDevServer.has(options)) {
23
+ const viteDevServer = await createServer(await this.createConfig(env, vitePlugin, options));
24
+ this._viteDevServer.set(options, viteDevServer);
43
25
  }
26
+ return this._viteDevServer.get(options);
44
27
  }
45
- async build(env) {
46
- log.info('Building UI...');
47
- const outDir = resolve(env.kopflos.basePath, this.outDir);
48
- const configPath = this.options.configPath
49
- ? resolve(env.kopflos.basePath, this.options.configPath)
50
- : this.options.configPath;
51
- await build(await prepareConfig({
52
- ...this.options,
28
+ createConfig(env, vitePlugin, options) {
29
+ const root = resolve(env.kopflos.basePath, options.root);
30
+ const outDir = this.resolveOutDir(env, options);
31
+ return prepareConfig({
32
+ ...options,
33
+ root,
53
34
  outDir,
54
- configPath,
55
- }));
35
+ config: vitePlugin.config,
36
+ configPath: vitePlugin.configPath,
37
+ });
38
+ }
39
+ resolveOutDir(env, options) {
40
+ return resolve(env.kopflos.basePath, env.kopflos.buildDir, options.outDir || '');
41
+ }
42
+ async beforeMiddleware(host, { env, plugins }) {
43
+ const vitePlugin = this.getDefaultPlugin(plugins);
44
+ for (const options of this.buildConfigurations) {
45
+ if (env.kopflos.config.mode === 'development') {
46
+ this.log.info('Development UI mode. Creating Vite server...');
47
+ const viteDevServer = await this.getViteDevServer(env, vitePlugin, options);
48
+ host.use(viteDevServer.middlewares);
49
+ }
50
+ else {
51
+ const buildDir = this.resolveOutDir(env, options);
52
+ this.log.info('Serving from build directory');
53
+ this.log.debug('Build directory:', buildDir);
54
+ host.use(express.static(buildDir));
55
+ }
56
+ }
57
+ }
58
+ async build(env, plugins) {
59
+ const vitePlugin = this.getDefaultPlugin(plugins);
60
+ for (const options of this.buildConfigurations) {
61
+ if (!options.entrypoints?.length) {
62
+ this.log.debug('No entrypoints specified. Skipping build');
63
+ return;
64
+ }
65
+ this.log.info('Building UI...');
66
+ const config = await this.createConfig(env, vitePlugin, options);
67
+ await build(config);
68
+ }
69
+ }
70
+ }
71
+ export default class DefaultPlugin extends VitePlugin {
72
+ constructor({ build = [], config, configPath }) {
73
+ if (!Array.isArray(build)) {
74
+ super('@kopflos-cms/vite', [build]);
75
+ this.buildConfiguration = build;
76
+ }
77
+ else {
78
+ super('@kopflos-cms/vite', build);
79
+ if (build.length === 1) {
80
+ this.buildConfiguration = build[0];
81
+ }
82
+ }
83
+ this.config = config;
84
+ this.configPath = configPath;
85
+ }
86
+ getDefaultPlugin() {
87
+ return this;
88
+ }
89
+ getDefaultViteDevServer(env) {
90
+ // only work when there is exactly one build configuration
91
+ if (!this.buildConfiguration) {
92
+ throw new Error('No build configuration found. Please add a build configuration to your vite plugin');
93
+ }
94
+ return this.getViteDevServer(env, this, this.buildConfiguration);
56
95
  }
57
96
  }
package/lib/config.d.ts CHANGED
@@ -1,2 +1,8 @@
1
- import type { Options } from '../index.js';
2
- export declare function prepareConfig({ root, configPath, entrypoints, outDir, config }: Omit<Options, 'mode'>): Promise<Record<string, any>>;
1
+ import type { InlineConfig } from 'vite';
2
+ import type { BuildConfiguration } from '../index.js';
3
+ type ConfigOptions = BuildConfiguration & {
4
+ configPath: string | undefined;
5
+ config: InlineConfig | undefined;
6
+ };
7
+ export declare function prepareConfig({ root, configPath, entrypoints, outDir, config }: ConfigOptions): Promise<Record<string, any>>;
8
+ export {};
package/lib/config.js CHANGED
@@ -10,11 +10,11 @@ export async function prepareConfig({ root, configPath, entrypoints, outDir, con
10
10
  },
11
11
  };
12
12
  if (outDir) {
13
- inputConfig.build.outDir = resolve(process.cwd(), outDir);
13
+ inputConfig.build.outDir = resolve(root, outDir);
14
14
  }
15
- if (entrypoints) {
15
+ if (entrypoints.length > 0) {
16
16
  inputConfig.build.rollupOptions = {
17
- input: entrypoints.flatMap(entry => glob.sync(entry)),
17
+ input: entrypoints.flatMap(entry => glob.sync(resolve(root, entry))),
18
18
  };
19
19
  }
20
20
  if (configPath) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kopflos-cms/vite",
3
- "version": "0.2.2",
3
+ "version": "0.3.0",
4
4
  "type": "module",
5
5
  "main": "index.js",
6
6
  "author": "Zazuko GmbH",
@@ -34,8 +34,8 @@
34
34
  "vite": "^6.4.1"
35
35
  },
36
36
  "devDependencies": {
37
- "@kopflos-cms/core": "^0.6.1",
38
- "@zazuko/env-node": "^2.0.0",
37
+ "@kopflos-cms/core": "^0.7.0",
38
+ "@zazuko/env-node": "^3.0.0",
39
39
  "@types/glob": "^8.1.0",
40
40
  "chai": "^5.1.1"
41
41
  },
package/template.d.ts CHANGED
@@ -1,2 +1,2 @@
1
1
  import type { Kopflos, SubjectHandler } from '@kopflos-cms/core';
2
- export declare const transform: (this: Kopflos) => SubjectHandler;
2
+ export declare const transform: (this: Kopflos, path: string) => SubjectHandler;
package/template.js CHANGED
@@ -1,34 +1,33 @@
1
- import { log } from './lib/log.js';
2
- export const transform = function () {
1
+ import * as fs from 'node:fs/promises';
2
+ import { resolve } from 'node:path';
3
+ import { createLogger } from '@kopflos-cms/logger';
4
+ const log = createLogger('template');
5
+ export const transform = function (path) {
6
+ const vitePlugin = this.getPlugin('@kopflos-cms/vite');
3
7
  const prepareDevTemplate = async (subject, template) => {
4
- const vite = this.getPlugin('@kopflos-cms/vite');
5
- if (!vite?.viteDevServer) {
6
- throw new Error('Vite dev server not initialized. Check vite plugin configuration.');
8
+ if (!vitePlugin) {
9
+ throw new Error('Vite plugin not found. Did you forget to add it to the config?');
7
10
  }
11
+ const viteDevServer = await vitePlugin.getDefaultViteDevServer(this.env);
8
12
  const subjectPath = new URL(subject.value).pathname;
9
- return vite.viteDevServer.transformIndexHtml(subjectPath, template);
13
+ return viteDevServer.transformIndexHtml(subjectPath, template);
10
14
  };
11
15
  return async ({ subject, env }, response) => {
12
- if (!isHtmlResponse(response)) {
13
- throw new Error('Vite handler must be chained after another which returns a HTML response');
14
- }
15
16
  if (env.kopflos.config.mode === 'production') {
16
- return response;
17
+ const template = await fs.readFile(resolve(env.kopflos.basePath, env.kopflos.buildDir, path));
18
+ return {
19
+ status: 200,
20
+ body: template.toString(),
21
+ headers: {
22
+ 'Content-Type': 'text/html',
23
+ },
24
+ };
17
25
  }
18
26
  log.debug('Compiling page template');
27
+ const template = await fs.readFile(resolve(env.kopflos.basePath, vitePlugin.buildConfiguration.root, path));
19
28
  return {
20
29
  ...response,
21
- body: await prepareDevTemplate(subject, response.body),
30
+ body: await prepareDevTemplate(subject, template.toString()),
22
31
  };
23
32
  };
24
33
  };
25
- function isHtmlResponse(response) {
26
- return typeof response?.body === 'string' || hasHeader(response?.headers, 'Content-Type', 'text/html');
27
- }
28
- function hasHeader(headers, headerName, headerValue) {
29
- const normalizedHeaderName = headerName.toLowerCase();
30
- return !!headers && Object.entries(headers)
31
- .some(([key, value]) => {
32
- return key.toLowerCase() === normalizedHeaderName && (value === headerValue || (Array.isArray(value) && value.includes(headerValue)));
33
- });
34
- }
package/lib/log.d.ts DELETED
@@ -1 +0,0 @@
1
- export declare const log: import("anylogger").Logger<import("anylogger").BaseLevels>;
package/lib/log.js DELETED
@@ -1,2 +0,0 @@
1
- import { createLogger } from '@kopflos-cms/logger';
2
- export const log = createLogger('vite');
package/lib/server.d.ts DELETED
@@ -1,2 +0,0 @@
1
- import type { Options } from '../index.js';
2
- export declare const createViteServer: (options: Pick<Options, "configPath" | "entrypoints">) => Promise<import("vite").ViteDevServer>;
package/lib/server.js DELETED
@@ -1,7 +0,0 @@
1
- import { createServer } from 'vite';
2
- import onetime from 'onetime';
3
- import { prepareConfig } from './config.js';
4
- export const createViteServer = onetime(async (options) => {
5
- const config = await prepareConfig(options);
6
- return createServer(config);
7
- });