@quilted/rollup 0.0.0-preview-20231014022518

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.
Files changed (55) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/LICENSE.md +21 -0
  3. package/README.md +1 -0
  4. package/build/cjs/app.cjs +158 -0
  5. package/build/cjs/constants.cjs +13 -0
  6. package/build/cjs/env.cjs +135 -0
  7. package/build/cjs/index.cjs +16 -0
  8. package/build/cjs/request-router.cjs +31 -0
  9. package/build/cjs/shared/magic-module.cjs +32 -0
  10. package/build/cjs/shared/rollup.cjs +15 -0
  11. package/build/cjs/shared/strings.cjs +16 -0
  12. package/build/esm/app.mjs +151 -0
  13. package/build/esm/constants.mjs +7 -0
  14. package/build/esm/env.mjs +112 -0
  15. package/build/esm/index.mjs +3 -0
  16. package/build/esm/request-router.mjs +29 -0
  17. package/build/esm/shared/magic-module.mjs +30 -0
  18. package/build/esm/shared/rollup.mjs +13 -0
  19. package/build/esm/shared/strings.mjs +14 -0
  20. package/build/esnext/app.esnext +151 -0
  21. package/build/esnext/constants.esnext +7 -0
  22. package/build/esnext/env.esnext +112 -0
  23. package/build/esnext/index.esnext +3 -0
  24. package/build/esnext/request-router.esnext +29 -0
  25. package/build/esnext/shared/magic-module.esnext +30 -0
  26. package/build/esnext/shared/rollup.esnext +13 -0
  27. package/build/esnext/shared/strings.esnext +14 -0
  28. package/build/tsconfig.tsbuildinfo +1 -0
  29. package/build/typescript/app.d.ts +163 -0
  30. package/build/typescript/app.d.ts.map +1 -0
  31. package/build/typescript/constants.d.ts +6 -0
  32. package/build/typescript/constants.d.ts.map +1 -0
  33. package/build/typescript/env.d.ts +55 -0
  34. package/build/typescript/env.d.ts.map +1 -0
  35. package/build/typescript/index.d.ts +4 -0
  36. package/build/typescript/index.d.ts.map +1 -0
  37. package/build/typescript/request-router.d.ts +15 -0
  38. package/build/typescript/request-router.d.ts.map +1 -0
  39. package/build/typescript/shared/magic-module.d.ts +19 -0
  40. package/build/typescript/shared/magic-module.d.ts.map +1 -0
  41. package/build/typescript/shared/rollup.d.ts +5 -0
  42. package/build/typescript/shared/rollup.d.ts.map +1 -0
  43. package/build/typescript/shared/strings.d.ts +2 -0
  44. package/build/typescript/shared/strings.d.ts.map +1 -0
  45. package/package.json +53 -0
  46. package/quilt.project.ts +5 -0
  47. package/source/app.ts +225 -0
  48. package/source/constants.ts +5 -0
  49. package/source/env.ts +176 -0
  50. package/source/index.ts +13 -0
  51. package/source/request-router.ts +34 -0
  52. package/source/shared/magic-module.ts +42 -0
  53. package/source/shared/rollup.ts +38 -0
  54. package/source/shared/strings.ts +17 -0
  55. package/tsconfig.json +10 -0
package/source/env.ts ADDED
@@ -0,0 +1,176 @@
1
+ import * as path from 'path';
2
+ import * as fs from 'fs';
3
+
4
+ import type {PluginContext} from 'rollup';
5
+
6
+ import {MAGIC_MODULE_ENV} from './constants.ts';
7
+ import {multiline} from './shared/strings.ts';
8
+ import {smartReplace} from './shared/rollup.ts';
9
+ import {createMagicModulePlugin} from './shared/magic-module.ts';
10
+
11
+ const EMPTY_PROCESS_ENV_OBJECT = {
12
+ 'globalThis.process.env.': `({}).`,
13
+ 'global.process.env.': `({}).`,
14
+ 'process.env.': `({}).`,
15
+ };
16
+
17
+ export function replaceProcessEnv({
18
+ mode,
19
+ preserve = true,
20
+ }: {
21
+ mode: string;
22
+ preserve?: boolean;
23
+ }) {
24
+ return smartReplace({
25
+ // @see https://github.com/vitejs/vite/blob/2b1ffe86328f9d06ef9528ee117b61889893ddcc/packages/vite/src/node/plugins/define.ts#L112
26
+ 'globalThis.process.env.NODE_ENV': JSON.stringify(mode),
27
+ 'global.process.env.NODE_ENV': JSON.stringify(mode),
28
+ 'process.env.NODE_ENV': JSON.stringify(mode),
29
+ ...(preserve ? {} : EMPTY_PROCESS_ENV_OBJECT),
30
+ });
31
+ }
32
+
33
+ export interface MagicModuleEnvOptions {
34
+ /**
35
+ * The runtime mode for your target environment.
36
+ */
37
+ mode?: 'production' | 'development';
38
+
39
+ /**
40
+ * Environment variables from the build environment to inline into the magic
41
+ * module. Be careful when using this option! Inlining environment variables
42
+ * into your application always comes with the risk of exposing sensitive information
43
+ * to your users. Only use this option for environment variables that are safe
44
+ * for a human to see if they open their browser developer tools.
45
+ */
46
+ inline?: string[];
47
+
48
+ /**
49
+ * A string that will be inlined directly as code to reference a runtime variable
50
+ * that contains environment variables.
51
+ */
52
+ runtime?: string;
53
+
54
+ /**
55
+ * Whether to load environment variables from a `.env` file. The option can
56
+ * be one of the following types:
57
+ *
58
+ * - `false`, which disables loading environment variables from a `.env` file.
59
+ * - An object containing a `roots` field, specifying the directories to search
60
+ * for `.env` files in.
61
+ * - An object containing a `files` field, specifying the directories to search
62
+ * for `.env` files in.
63
+ *
64
+ * @default {roots: ['.', 'configuration']}
65
+ */
66
+ dotenv?:
67
+ | false
68
+ | {roots?: string[]; files?: never}
69
+ | {roots?: never; files?: string[]};
70
+ }
71
+
72
+ export function magicModuleEnv({
73
+ mode,
74
+ dotenv = {roots: ['.', 'configuration']},
75
+ inline = [],
76
+ runtime = '{}',
77
+ }: MagicModuleEnvOptions = {}) {
78
+ return createMagicModulePlugin({
79
+ name: '@quilted/magic-module/env',
80
+ module: MAGIC_MODULE_ENV,
81
+ async source() {
82
+ const inlineEnv: Record<string, string> = {};
83
+
84
+ if (mode) {
85
+ inlineEnv.MODE = mode;
86
+ }
87
+
88
+ const loadedEnv = await loadEnv.call(this, {mode, dotenv});
89
+
90
+ for (const inlineVariable of inline.sort()) {
91
+ if (inlineVariable in inlineEnv) continue;
92
+ const value = process.env[inlineVariable] ?? loadedEnv[inlineVariable];
93
+ if (value == null) continue;
94
+ inlineEnv[inlineVariable] =
95
+ typeof value === 'string' &&
96
+ value[0] === '"' &&
97
+ value[value.length - 1] === '"'
98
+ ? JSON.parse(value)
99
+ : value;
100
+ }
101
+
102
+ return multiline`
103
+ const runtime = (${runtime});
104
+ const inline = JSON.parse(${JSON.stringify(JSON.stringify(inlineEnv))});
105
+
106
+ const Env = new Proxy(
107
+ {},
108
+ {
109
+ get(_, property) {
110
+ return runtime[property] ?? inline[property];
111
+ },
112
+ },
113
+ );
114
+
115
+ export default Env;
116
+ `;
117
+ },
118
+ });
119
+ }
120
+
121
+ // Inspired by https://github.com/vitejs/vite/blob/e0a4d810598d1834933ed437ac5a2168cbbbf2f8/packages/vite/source/node/config.ts#L1050-L1113
122
+ async function loadEnv(
123
+ this: PluginContext,
124
+ {
125
+ mode,
126
+ dotenv,
127
+ }: {mode?: string} & Required<Pick<MagicModuleEnvOptions, 'dotenv'>>,
128
+ ): Promise<Record<string, string | undefined>> {
129
+ const env: Record<string, string | undefined> = {...process.env};
130
+
131
+ if (dotenv !== false) {
132
+ const {parse} = await import('dotenv');
133
+
134
+ let files = dotenv.files;
135
+
136
+ if (files == null) {
137
+ const testFiles = [
138
+ // default file
139
+ `.env`,
140
+ // local file
141
+ `.env.local`,
142
+ ];
143
+
144
+ if (mode) {
145
+ testFiles.push(
146
+ // mode file
147
+ `.env.${mode}`,
148
+ // mode local file
149
+ `.env.${mode}.local`,
150
+ );
151
+ }
152
+
153
+ files = testFiles.flatMap((file) =>
154
+ (dotenv.roots ?? ['.', 'configuration']).map((root) =>
155
+ path.resolve(root, file),
156
+ ),
157
+ );
158
+ }
159
+
160
+ const envFileResults = await Promise.all(
161
+ files.map(async (file) => {
162
+ if (fs.existsSync(file)) {
163
+ this.addWatchFile(file);
164
+ return parse(await fs.promises.readFile(file, 'utf-8'));
165
+ }
166
+ }),
167
+ );
168
+
169
+ for (const envFileResult of envFileResults) {
170
+ if (envFileResult == null) continue;
171
+ Object.assign(env, envFileResult);
172
+ }
173
+ }
174
+
175
+ return env;
176
+ }
@@ -0,0 +1,13 @@
1
+ export {magicModuleEnv, type MagicModuleEnvOptions} from './env.ts';
2
+ export {
3
+ quiltApp,
4
+ quiltAppBrowser,
5
+ quiltAppServer,
6
+ magicModuleAppComponent,
7
+ magicModuleAppBrowserEntry,
8
+ magicModuleAppRequestRouter,
9
+ type AppOptions,
10
+ type AppBrowserOptions,
11
+ type AppServerOptions,
12
+ } from './app.ts';
13
+ export {magicModuleRequestRouterEntry} from './request-router.ts';
@@ -0,0 +1,34 @@
1
+ import {MAGIC_MODULE_ENTRY, MAGIC_MODULE_REQUEST_ROUTER} from './constants.ts';
2
+
3
+ import {createMagicModulePlugin} from './shared/magic-module.ts';
4
+ import {multiline} from './shared/strings.ts';
5
+
6
+ export function magicModuleRequestRouterEntry({
7
+ host,
8
+ port,
9
+ }: {
10
+ host?: string;
11
+ port?: number;
12
+ } = {}) {
13
+ return createMagicModulePlugin({
14
+ name: '@quilted/request-router',
15
+ sideEffects: true,
16
+ module: MAGIC_MODULE_ENTRY,
17
+ async source() {
18
+ const initialContent = multiline`
19
+ import requestRouter from ${JSON.stringify(
20
+ MAGIC_MODULE_REQUEST_ROUTER,
21
+ )};
22
+
23
+ import {createHttpServer} from '@quilted/quilt/request-router/node';
24
+
25
+ const port = ${port ?? 'Number.parseInt(process.env.PORT, 10)'};
26
+ const host = ${host ? JSON.stringify(host) : 'process.env.HOST'};
27
+
28
+ createHttpServer(requestRouter).listen(port, host);
29
+ `;
30
+
31
+ return initialContent;
32
+ },
33
+ });
34
+ }
@@ -0,0 +1,42 @@
1
+ import type {Plugin, PluginContext} from 'rollup';
2
+
3
+ const VIRTUAL_MODULE_PREFIX = '\0';
4
+ const VIRTUAL_MODULE_POSTFIX = '/module.js';
5
+
6
+ export function createMagicModulePlugin({
7
+ name,
8
+ module,
9
+ alias = `${VIRTUAL_MODULE_PREFIX}${module}${VIRTUAL_MODULE_POSTFIX}`,
10
+ source: getSource,
11
+ sideEffects = false,
12
+ }: {
13
+ readonly name: string;
14
+ readonly alias?: string;
15
+ readonly module: string;
16
+ readonly sideEffects?: boolean;
17
+ source?(this: PluginContext): string | Promise<string>;
18
+ }) {
19
+ return {
20
+ name,
21
+ resolveId(id) {
22
+ if (id !== module) return null;
23
+
24
+ return {
25
+ id: alias,
26
+ moduleSideEffects: sideEffects ? 'no-treeshake' : undefined,
27
+ };
28
+ },
29
+ load: getSource
30
+ ? async function load(source) {
31
+ if (source !== alias) return null;
32
+
33
+ const code = await getSource.call(this);
34
+
35
+ return {
36
+ code,
37
+ moduleSideEffects: sideEffects ? 'no-treeshake' : undefined,
38
+ };
39
+ }
40
+ : undefined,
41
+ } satisfies Plugin;
42
+ }
@@ -0,0 +1,38 @@
1
+ import type {InputOptions} from 'rollup';
2
+ import replace, {type RollupReplaceOptions} from '@rollup/plugin-replace';
3
+
4
+ export function smartReplace(
5
+ values: NonNullable<RollupReplaceOptions['values']>,
6
+ options?: Omit<RollupReplaceOptions, 'values'>,
7
+ ) {
8
+ return replace({
9
+ // @see https://github.com/vitejs/vite/blob/2b1ffe86328f9d06ef9528ee117b61889893ddcc/packages/vite/src/node/plugins/define.ts#L108-L119
10
+ delimiters: [
11
+ '(?<![\\p{L}\\p{N}_$]|(?<!\\.\\.)\\.)(',
12
+ ')(?:(?<=\\.)|(?![\\p{L}\\p{N}_$]|\\s*?=[^=]))',
13
+ ],
14
+ preventAssignment: true,
15
+ ...options,
16
+ values,
17
+ });
18
+ }
19
+
20
+ export function addRollupOnWarn(
21
+ options: InputOptions,
22
+ warn: NonNullable<InputOptions['onwarn']>,
23
+ ): InputOptions {
24
+ const {onwarn: originalOnWarn} = options;
25
+
26
+ return {
27
+ ...options,
28
+ onwarn(warning, defaultWarn) {
29
+ warn(warning, (warning) => {
30
+ if (originalOnWarn && typeof warning === 'object') {
31
+ originalOnWarn(warning, defaultWarn);
32
+ } else {
33
+ defaultWarn(warning);
34
+ }
35
+ });
36
+ },
37
+ };
38
+ }
@@ -0,0 +1,17 @@
1
+ export function multiline(strings: TemplateStringsArray, ...values: any[]) {
2
+ let result = strings.reduce(
3
+ (combined, string, index) => `${combined}${string}${values[index] ?? ''}`,
4
+ '',
5
+ );
6
+
7
+ // Inspired by https://github.com/zspecza/common-tags/blob/master/src/stripIndentTransformer/stripIndentTransformer.js#L8
8
+ const match = result.match(/^[^\S\n]*(?=\S)/gm);
9
+ const indent = match && Math.min(...match.map((indent) => indent.length));
10
+
11
+ if (indent) {
12
+ const regexp = new RegExp(`^.{${indent}}`, 'gm');
13
+ result = result.replace(regexp, '');
14
+ }
15
+
16
+ return result.trim();
17
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,10 @@
1
+ {
2
+ "extends": "@quilted/typescript/project.json",
3
+ "compilerOptions": {
4
+ "rootDir": "source",
5
+ "outDir": "build/typescript"
6
+ },
7
+ "include": ["source"],
8
+ "exclude": ["quilt.project.ts", "**/*.test.ts", "**/*.test.tsx"],
9
+ "references": []
10
+ }