Skip to content

Commit

Permalink
feat: add env module, resolve #852
Browse files Browse the repository at this point in the history
  • Loading branch information
yjl9903 committed Oct 1, 2024
1 parent c182c3f commit e59ff4f
Show file tree
Hide file tree
Showing 10 changed files with 150 additions and 68 deletions.
3 changes: 3 additions & 0 deletions examples/nuxt/app.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
<script setup lang="ts">
import { message } from '~build/meta';
import { BUILD_MESSAGE } from '~build/env';
console.log('BUILD_MESSAGE:', BUILD_MESSAGE);
</script>

<template>
Expand Down
4 changes: 4 additions & 0 deletions examples/nuxt/env.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,7 @@
declare module '~build/meta' {
export const message: string;
}

declare module '~build/env' {
export const BUILD_MESSAGE: string;
}
3 changes: 3 additions & 0 deletions examples/nuxt/nuxt.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ export default defineNuxtConfig({
info: {
meta: {
message: 'This is from nuxt.config.ts'
},
env: {
BUILD_MESSAGE: 'invalid'
}
}
});
2 changes: 1 addition & 1 deletion examples/nuxt/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"build": "nuxi build",
"dev": "nuxi dev",
"generate": "nuxi generate",
"start": "nuxi preview"
"preview": "nuxi preview"
},
"devDependencies": {
"nuxt": "^3.10.1",
Expand Down
130 changes: 69 additions & 61 deletions src/core/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,81 +10,89 @@ import { BuildGitModule } from './modules/git';
import { LegacyInfoModule } from './modules/info';
import { BuildCIModule } from './modules/ci';
import { BuildMetaModule } from './modules/meta';
import { BuildEnvModule } from './modules/env';
import { BuildPackageModule } from './modules/package';

export * from './types';

export const UnpluginInfo = createUnplugin<Options | undefined>((options = {}) => {
const root = path.resolve(options?.root ?? process.cwd());
export const UnpluginInfo = /* #__PURE__ */ createUnplugin<Options | undefined>(
(options = {}, meta) => {
const root = path.resolve(options?.root ?? process.cwd());

const modules = {
Time: new BuildTimeModule(root, options),
Git: new BuildGitModule(root, options),
Info: new LegacyInfoModule(root, options),
CI: new BuildCIModule(root, options),
Meta: new BuildMetaModule(root, options),
Package: new BuildPackageModule(root, options)
};
const modules = {
Time: new BuildTimeModule(root, options),
Git: new BuildGitModule(root, options),
Info: new LegacyInfoModule(root, options),
CI: new BuildCIModule(root, options),
Meta: new BuildMetaModule(root, options),
Env: new BuildEnvModule(root, options),
Package: new BuildPackageModule(root, options)
};

return {
name: 'unplugin-info',
async buildStart() {
await Promise.all(Object.values(modules).map((mod) => mod.buildStart(this)));
},
async buildEnd() {
await Promise.all(Object.values(modules).map((mod) => mod.buildEnd(this)));
},
resolveId(id) {
if (
Object.values(modules)
return {
name: 'unplugin-info',
async buildStart() {
await Promise.all(Object.values(modules).map((mod) => mod.buildStart(this)));
},
async buildEnd() {
await Promise.all(Object.values(modules).map((mod) => mod.buildEnd(this)));
},
resolveId(id) {
if (
Object.values(modules)
.map((m) => m.name)
.includes(id)
) {
return `\0${id}`;
}
},
loadInclude(id) {
if (!id.startsWith('\0')) return false;
id = id.slice(1);
return Object.values(modules)
.map((m) => m.name)
.includes(id)
) {
return `\0${id}`;
}
},
loadInclude(id) {
if (!id.startsWith('\0')) return false;
id = id.slice(1);
return Object.values(modules)
.map((m) => m.name)
.includes(id);
},
async load(id) {
if (!id.startsWith('\0')) return;
id = id.slice(1);
.includes(id);
},
async load(id) {
if (!id.startsWith('\0')) return;
id = id.slice(1);

for (const mod of Object.values(modules)) {
if (id === mod.name) {
if (id === modules.Info.name) {
this.warn(
`${modules.Info.name} is deprecated, please migrate to ${modules.Git.name} and ${modules.CI.name}`
);
}
for (const mod of Object.values(modules)) {
if (id === mod.name) {
if (id === modules.Info.name) {
this.warn(
`${modules.Info.name} is deprecated, please migrate to ${modules.Git.name} and ${modules.CI.name}.`
);
}
if (id === modules.Env.name && meta.framework !== 'vite') {
this.warn(`${modules.Env.name} is only supported in Vite.`);
return;
}

return mod.load(this, id);
return mod.load(this, id);
}
}
}
},
vite: {
handleHotUpdate({ file, server }) {
// HMR: package.json
if (file === normalizePath(path.resolve(root, 'package.json'))) {
const module = server.moduleGraph.getModuleById('\0' + modules.Package.name);
if (module) {
// Invalidate module for reloading
server.moduleGraph.invalidateModule(module);
},
vite: {
handleHotUpdate({ file, server }) {
// HMR: package.json
if (file === normalizePath(path.resolve(root, 'package.json'))) {
const module = server.moduleGraph.getModuleById('\0' + modules.Package.name);
if (module) {
// Invalidate module for reloading
server.moduleGraph.invalidateModule(module);

// Reload client
server.ws.send({
type: 'full-reload'
});
// Reload client
server.ws.send({
type: 'full-reload'
});
}
}
}
}
}
};
});
};
}
);

function normalizePath(filename: string) {
return filename.split(path.win32.sep).join(path.posix.sep);
Expand Down
26 changes: 26 additions & 0 deletions src/core/modules/env.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { type Options, BuildInfoModule } from '../types';

export class BuildEnvModule extends BuildInfoModule {
constructor(root: string, options: Options) {
super('env', root, options);
}

async load() {
const { options } = this;
const get = () => {
if (!options?.env) return {};
if (typeof options.env === 'function') {
return options.env();
}
return options.env;
};

const meta = await get();
const body = Object.entries(meta).map(
([key, value]) =>
`export const ${key} = (import.meta.env.SSR ? process?.env?.['${key.replace(/'/g, '\\')}'] : undefined) ?? ${JSON.stringify(value, null, 2)};`
);

return body.length > 0 ? body.join('\n') : 'export {};';
}
}
7 changes: 5 additions & 2 deletions src/core/modules/meta.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,12 @@ export class BuildMetaModule extends BuildInfoModule {
}
return options.meta;
};
const body = Object.entries(await get()).map(

const meta = await get();
const body = Object.entries(meta).map(
([key, value]) => `export const ${key} = ${JSON.stringify(value, null, 2)};`
);
return body.join('\n');

return body.length > 0 ? body.join('\n') : 'export {};';
}
}
15 changes: 13 additions & 2 deletions src/core/types.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import type { SimpleGit } from 'simple-git';
import type { UnpluginBuildContext, UnpluginContext, TransformResult } from 'unplugin';

type Metadata = Record<string | number, any>;

export abstract class BuildInfoModule {
name: string;

Expand All @@ -26,6 +24,10 @@ export abstract class BuildInfoModule {
): TransformResult | Promise<TransformResult>;
}

type Metadata = Record<string | number, any>;

type Env = Record<string | number, any>;

export interface Options {
/**
* Git repo root path
Expand Down Expand Up @@ -54,6 +56,15 @@ export interface Options {
*/
meta?: Metadata | (() => Metadata | Promise<Metadata>);

/**
* Pass environment variables to Vite SSR app
*
* For each key / value record
* - In the SSR environment, it will read the environment variable (e.g. process.env.<key>)
* - In the client environment, it will return the provided default value
*/
env?: Metadata | (() => Env | Promise<Env>);

/**
* Custom virtual module prefix
*
Expand Down
18 changes: 17 additions & 1 deletion test/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { describe, expect, it } from 'vitest';
import { beforeAll, describe, expect, it } from 'vitest';

import '../client.d';

Expand All @@ -11,6 +11,7 @@ describe('build timestamp', () => {

describe('build info', () => {
it('should work', async () => {
// @ts-ignore
const { github } = await import('~build/info');
expect(github).toMatchInlineSnapshot('"https://github.com/yjl9903/unplugin-info"');
});
Expand All @@ -36,3 +37,18 @@ describe('build meta', () => {
expect(message).toMatchInlineSnapshot('"This is set from vite.config.ts"');
});
});

describe('build env', () => {
const MESSAGE = 'This is set from the vitest';

beforeAll(() => {
// @ts-ignore
process.env.BUILD_MESSAGE = MESSAGE;
});

it('should work', async () => {
// @ts-ignore
const { BUILD_MESSAGE: message } = await import('~build/env');
expect(message).toStrictEqual(MESSAGE);
});
});
10 changes: 9 additions & 1 deletion vitest.config.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
import { defineConfig } from 'vitest/config';

import Info from './src/vite';

export default defineConfig({
test: {},
plugins: [Info({ meta: { message: 'This is set from vite.config.ts' } })]
plugins: [
Info({
meta: { message: 'This is set from vite.config.ts' },
env: {
BUILD_MESSAGE: 'This should be overwriten in SSR'
}
})
]
});

0 comments on commit e59ff4f

Please sign in to comment.