@quilted/rollup 0.1.1 → 0.1.3

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,21 @@
1
1
  # @quilted/rollup
2
2
 
3
+ ## 0.1.3
4
+
5
+ ### Patch Changes
6
+
7
+ - [`4841dca5`](https://github.com/lemonmade/quilt/commit/4841dca5fb9817c8873b06c1790b3f4a05bc62d6) Thanks [@lemonmade](https://github.com/lemonmade)! - Add GraphQL plugins
8
+
9
+ - [`34b06933`](https://github.com/lemonmade/quilt/commit/34b06933ea46eedace0caef682ae6aadea3f4ea8) Thanks [@lemonmade](https://github.com/lemonmade)! - Add output options
10
+
11
+ ## 0.1.2
12
+
13
+ ### Patch Changes
14
+
15
+ - [`27496819`](https://github.com/lemonmade/quilt/commit/274968195084a07507f67622c441aaabc2c429e7) Thanks [@lemonmade](https://github.com/lemonmade)! - Add Node plugins
16
+
17
+ - [`b481f939`](https://github.com/lemonmade/quilt/commit/b481f9399548b61096caf093d77658b9c9d2a789) Thanks [@lemonmade](https://github.com/lemonmade)! - Add visualizer and minify plugin
18
+
3
19
  ## 0.1.1
4
20
 
5
21
  ### Patch Changes
package/build/cjs/app.cjs CHANGED
@@ -1,21 +1,49 @@
1
1
  'use strict';
2
2
 
3
+ var path = require('node:path');
3
4
  var constants = require('./constants.cjs');
4
5
  var strings = require('./shared/strings.cjs');
6
+ var rollup = require('./shared/rollup.cjs');
5
7
  var magicModule = require('./shared/magic-module.cjs');
6
8
 
7
- function quiltApp({
9
+ function _interopNamespaceDefault(e) {
10
+ var n = Object.create(null);
11
+ if (e) {
12
+ Object.keys(e).forEach(function (k) {
13
+ if (k !== 'default') {
14
+ var d = Object.getOwnPropertyDescriptor(e, k);
15
+ Object.defineProperty(n, k, d.get ? d : {
16
+ enumerable: true,
17
+ get: function () { return e[k]; }
18
+ });
19
+ }
20
+ });
21
+ }
22
+ n["default"] = e;
23
+ return Object.freeze(n);
24
+ }
25
+
26
+ var path__namespace = /*#__PURE__*/_interopNamespaceDefault(path);
27
+
28
+ function quiltAppBrowser({
29
+ app,
8
30
  env,
9
- entry
31
+ assets,
32
+ module,
33
+ graphql = true
10
34
  } = {}) {
11
35
  return {
12
- name: '@quilted/app',
36
+ name: '@quilted/app/browser',
13
37
  async options(originalOptions) {
14
- const newPlugins = [...(Array.isArray(originalOptions.plugins) ? originalOptions.plugins : originalOptions.plugins ? [originalOptions.plugins] : [])];
38
+ const newPlugins = rollup.rollupPluginsToArray(originalOptions.plugins);
15
39
  const newOptions = {
16
40
  ...originalOptions,
17
41
  plugins: newPlugins
18
42
  };
43
+ const [{
44
+ visualizer
45
+ }, nodePlugins] = await Promise.all([import('rollup-plugin-visualizer'), rollup.getNodePlugins()]);
46
+ newPlugins.push(...nodePlugins);
19
47
  if (env) {
20
48
  const {
21
49
  magicModuleEnv,
@@ -38,44 +66,116 @@ function quiltApp({
38
66
  }));
39
67
  }
40
68
  }
41
- if (entry) {
69
+ if (app) {
42
70
  newPlugins.push(magicModuleAppComponent({
43
- entry
71
+ entry: app
72
+ }));
73
+ }
74
+ newPlugins.push(magicModuleAppBrowserEntry(module));
75
+ if (graphql) {
76
+ const {
77
+ graphql
78
+ } = await Promise.resolve().then(function () { return require('./graphql.cjs'); });
79
+ newPlugins.push(graphql({
80
+ manifest: path__namespace.resolve(`manifests/graphql.json`)
44
81
  }));
45
82
  }
83
+ const minify = assets?.minify ?? true;
84
+ if (minify) {
85
+ const {
86
+ minify
87
+ } = await import('rollup-plugin-esbuild');
88
+ newPlugins.push(minify());
89
+ }
90
+ newPlugins.push(visualizer({
91
+ template: 'treemap',
92
+ open: false,
93
+ brotliSize: true,
94
+ filename: path__namespace.resolve(`reports/bundle-visualizer.html`)
95
+ }));
46
96
  return newOptions;
47
- }
48
- };
49
- }
50
- function quiltAppBrowser(options = {}) {
51
- return {
52
- name: '@quilted/app/browser',
53
- options(originalOptions) {
54
- const newPlugins = [...(Array.isArray(originalOptions.plugins) ? originalOptions.plugins : originalOptions.plugins ? [originalOptions.plugins] : [])];
55
- const newOptions = {
97
+ },
98
+ outputOptions(originalOptions) {
99
+ return {
56
100
  ...originalOptions,
57
- plugins: newPlugins
101
+ // format: isESM ? 'esm' : 'systemjs',
102
+ format: 'esm',
103
+ dir: path__namespace.resolve(`build/assets`),
104
+ entryFileNames: `app.[hash].js`,
105
+ assetFileNames: `[name].[hash].[ext]`,
106
+ chunkFileNames: `[name].[hash].js`,
107
+ manualChunks: createManualChunksSorter()
58
108
  };
59
- newPlugins.push(magicModuleAppBrowserEntry(options));
60
- return newOptions;
61
109
  }
62
110
  };
63
111
  }
64
- function quiltAppServer(options = {}) {
112
+ function quiltAppServer({
113
+ app,
114
+ env,
115
+ graphql,
116
+ entry
117
+ } = {}) {
65
118
  return {
66
119
  name: '@quilted/app/server',
67
120
  async options(originalOptions) {
68
- const newPlugins = [...(Array.isArray(originalOptions.plugins) ? originalOptions.plugins : originalOptions.plugins ? [originalOptions.plugins] : [])];
69
- const [{
70
- magicModuleRequestRouterEntry
71
- }] = await Promise.all([Promise.resolve().then(function () { return require('./request-router.cjs'); })]);
121
+ const newPlugins = rollup.rollupPluginsToArray(originalOptions.plugins);
72
122
  const newOptions = {
73
123
  ...originalOptions,
74
124
  plugins: newPlugins
75
125
  };
126
+ const [{
127
+ magicModuleRequestRouterEntry
128
+ }, nodePlugins] = await Promise.all([Promise.resolve().then(function () { return require('./request-router.cjs'); }), rollup.getNodePlugins()]);
129
+ newPlugins.push(...nodePlugins);
130
+ if (env) {
131
+ const {
132
+ magicModuleEnv,
133
+ replaceProcessEnv
134
+ } = await Promise.resolve().then(function () { return require('./env.cjs'); });
135
+ if (typeof env === 'boolean') {
136
+ newPlugins.push(replaceProcessEnv({
137
+ mode: 'production'
138
+ }));
139
+ newPlugins.push(magicModuleEnv({
140
+ mode: 'production'
141
+ }));
142
+ } else {
143
+ newPlugins.push(replaceProcessEnv({
144
+ mode: env.mode ?? 'production'
145
+ }));
146
+ newPlugins.push(magicModuleEnv({
147
+ mode: 'production',
148
+ ...env
149
+ }));
150
+ }
151
+ }
152
+ if (app) {
153
+ newPlugins.push(magicModuleAppComponent({
154
+ entry: app
155
+ }));
156
+ }
76
157
  newPlugins.push(magicModuleRequestRouterEntry());
77
- newPlugins.push(magicModuleAppRequestRouter(options));
158
+ newPlugins.push(magicModuleAppRequestRouter({
159
+ entry
160
+ }));
161
+ if (graphql) {
162
+ const {
163
+ graphql
164
+ } = await Promise.resolve().then(function () { return require('./graphql.cjs'); });
165
+ newPlugins.push(graphql({
166
+ manifest: false
167
+ }));
168
+ }
78
169
  return newOptions;
170
+ },
171
+ outputOptions(originalOptions) {
172
+ return {
173
+ ...originalOptions,
174
+ // format,
175
+ format: 'esm',
176
+ dir: path__namespace.resolve(`build/server`),
177
+ entryFileNames: 'server.js'
178
+ };
79
179
  }
80
180
  };
81
181
  }
@@ -149,10 +249,75 @@ function magicModuleAppBrowserEntry({
149
249
  }
150
250
  });
151
251
  }
252
+ const FRAMEWORK_CHUNK_NAME = 'framework';
253
+ const POLYFILLS_CHUNK_NAME = 'polyfills';
254
+ const VENDOR_CHUNK_NAME = 'vendor';
255
+ const INTERNALS_CHUNK_NAME = 'internals';
256
+ const SHARED_CHUNK_NAME = 'shared';
257
+ const PACKAGES_CHUNK_NAME = 'packages';
258
+ const GLOBAL_CHUNK_NAME = 'global';
259
+ const FRAMEWORK_TEST_STRINGS = ['/node_modules/preact/', '/node_modules/react/', '/node_modules/js-cookie/', '/node_modules/@quilted/quilt/', '/node_modules/@preact/signals/', '/node_modules/@preact/signals-core/',
260
+ // TODO I should turn this into an allowlist
261
+ /node_modules[/]@quilted[/](?!react-query|swr)/];
262
+ const POLYFILL_TEST_STRINGS = ['/node_modules/@quilted/polyfills/', '/node_modules/core-js/', '/node_modules/whatwg-fetch/', '/node_modules/regenerator-runtime/', '/node_modules/abort-controller/'];
263
+ const INTERNALS_TEST_STRINGS = ['\x00commonjsHelpers.js', '/node_modules/@babel/runtime/'];
264
+
265
+ // When building from source, quilt packages are not in node_modules,
266
+ // so we instead add their repo paths to the list of framework test strings.
267
+ if (process.env.QUILT_FROM_SOURCE) {
268
+ FRAMEWORK_TEST_STRINGS.push('/quilt/packages/');
269
+ }
270
+
271
+ // Inspired by Vite: https://github.com/vitejs/vite/blob/c69f83615292953d40f07b1178d1ed1d72abe695/packages/vite/source/node/build.ts#L567
272
+ function createManualChunksSorter() {
273
+ // TODO: make this more configurable, and make it so that we bundle more intelligently
274
+ // for split entries
275
+ const packagesPath = path__namespace.resolve('packages') + path__namespace.sep;
276
+ const globalPath = path__namespace.resolve('global') + path__namespace.sep;
277
+ const sharedPath = path__namespace.resolve('shared') + path__namespace.sep;
278
+ return (id, {
279
+ getModuleInfo
280
+ }) => {
281
+ if (INTERNALS_TEST_STRINGS.some(test => id.includes(test))) {
282
+ return INTERNALS_CHUNK_NAME;
283
+ }
284
+ if (FRAMEWORK_TEST_STRINGS.some(test => typeof test === 'string' ? id.includes(test) : test.test(id))) {
285
+ return FRAMEWORK_CHUNK_NAME;
286
+ }
287
+ if (POLYFILL_TEST_STRINGS.some(test => id.includes(test))) {
288
+ return POLYFILLS_CHUNK_NAME;
289
+ }
290
+ let bundleBaseName;
291
+ let relativeId;
292
+ if (id.includes('/node_modules/')) {
293
+ const moduleInfo = getModuleInfo(id);
294
+
295
+ // If the only dependency is another vendor, let Rollup handle the naming
296
+ if (moduleInfo == null) return;
297
+ if (moduleInfo.importers.length > 0 && moduleInfo.importers.every(importer => importer.includes('/node_modules/'))) {
298
+ return;
299
+ }
300
+ bundleBaseName = VENDOR_CHUNK_NAME;
301
+ relativeId = id.replace(/^.*[/]node_modules[/]/, '');
302
+ } else if (id.startsWith(packagesPath)) {
303
+ bundleBaseName = PACKAGES_CHUNK_NAME;
304
+ relativeId = id.replace(packagesPath, '');
305
+ } else if (id.startsWith(globalPath)) {
306
+ bundleBaseName = GLOBAL_CHUNK_NAME;
307
+ relativeId = id.replace(globalPath, '');
308
+ } else if (id.startsWith(sharedPath)) {
309
+ bundleBaseName = SHARED_CHUNK_NAME;
310
+ relativeId = id.replace(sharedPath, '');
311
+ }
312
+ if (bundleBaseName == null || relativeId == null) {
313
+ return;
314
+ }
315
+ return `${bundleBaseName}-${relativeId.split(path__namespace.sep)[0]?.split('.')[0]}`;
316
+ };
317
+ }
152
318
 
153
319
  exports.magicModuleAppBrowserEntry = magicModuleAppBrowserEntry;
154
320
  exports.magicModuleAppComponent = magicModuleAppComponent;
155
321
  exports.magicModuleAppRequestRouter = magicModuleAppRequestRouter;
156
- exports.quiltApp = quiltApp;
157
322
  exports.quiltAppBrowser = quiltAppBrowser;
158
323
  exports.quiltAppServer = quiltAppServer;
@@ -0,0 +1,186 @@
1
+ 'use strict';
2
+
3
+ var node_crypto = require('node:crypto');
4
+ var graphql = require('graphql');
5
+
6
+ const IMPORT_REGEX = /^#import\s+['"]([^'"]*)['"];?[\s\n]*/gm;
7
+ const DEFAULT_NAME = 'Operation';
8
+ function cleanGraphQLDocument(document, {
9
+ removeUnused = true
10
+ } = {}) {
11
+ if (removeUnused) {
12
+ removeUnusedDefinitions(document, {
13
+ exclude: removeUnused === true ? new Set() : removeUnused.exclude
14
+ });
15
+ }
16
+ for (const definition of document.definitions) {
17
+ addTypename(definition);
18
+ }
19
+ const normalizedSource = minifyGraphQLSource(graphql.print(document));
20
+ const normalizedDocument = graphql.parse(normalizedSource);
21
+ for (const definition of normalizedDocument.definitions) {
22
+ stripLoc(definition);
23
+ }
24
+
25
+ // This ID is a hash of the full file contents that are part of the document,
26
+ // including other documents that are injected in, but excluding any unused
27
+ // fragments. This is useful for things like persisted queries.
28
+ const id = node_crypto.createHash('sha256').update(normalizedSource).digest('hex');
29
+ Reflect.defineProperty(normalizedDocument, 'id', {
30
+ value: id,
31
+ enumerable: true,
32
+ writable: false,
33
+ configurable: false
34
+ });
35
+ Reflect.defineProperty(normalizedDocument, 'loc', {
36
+ value: stripDocumentLoc(normalizedDocument.loc),
37
+ enumerable: true,
38
+ writable: false,
39
+ configurable: false
40
+ });
41
+ return normalizedDocument;
42
+ }
43
+ function extractGraphQLImports(rawSource) {
44
+ const imports = new Set();
45
+ const source = rawSource.replace(IMPORT_REGEX, (_, imported) => {
46
+ imports.add(imported);
47
+ return '';
48
+ });
49
+ return {
50
+ imports: [...imports],
51
+ source
52
+ };
53
+ }
54
+ function toGraphQLOperation(documentOrSource) {
55
+ const document = typeof documentOrSource === 'string' ? cleanGraphQLDocument(graphql.parse(documentOrSource)) : documentOrSource;
56
+ return {
57
+ id: document.id,
58
+ name: operationNameForDocument(document),
59
+ source: document.loc.source.body
60
+ };
61
+ }
62
+ function operationNameForDocument(document) {
63
+ return document.definitions.find(definition => definition.kind === 'OperationDefinition')?.name?.value;
64
+ }
65
+ function removeUnusedDefinitions(document, {
66
+ exclude
67
+ }) {
68
+ const usedDefinitions = new Set();
69
+ const dependencies = definitionDependencies(document.definitions);
70
+ const markAsUsed = definition => {
71
+ if (usedDefinitions.has(definition)) {
72
+ return;
73
+ }
74
+ usedDefinitions.add(definition);
75
+ for (const dependency of dependencies.get(definition) || []) {
76
+ markAsUsed(dependency);
77
+ }
78
+ };
79
+ for (const definition of document.definitions) {
80
+ if (definition.kind === 'FragmentDefinition') {
81
+ if (exclude.has(definition.name.value)) {
82
+ markAsUsed(definition);
83
+ }
84
+ } else {
85
+ markAsUsed(definition);
86
+ }
87
+ }
88
+ document.definitions = [...usedDefinitions];
89
+ }
90
+ function definitionDependencies(definitions) {
91
+ const executableDefinitions = definitions.filter(definition => definition.kind === 'OperationDefinition' || definition.kind === 'FragmentDefinition');
92
+ const definitionsByName = new Map(executableDefinitions.map(definition => [definition.name ? definition.name.value : DEFAULT_NAME, definition]));
93
+ return new Map(executableDefinitions.map(executableNode => [executableNode, [...collectUsedFragmentSpreads(executableNode, new Set())].map(usedFragment => {
94
+ const definition = definitionsByName.get(usedFragment);
95
+ if (definition == null) {
96
+ throw new Error(`You attempted to use the fragment '${usedFragment}' (in '${executableNode.name ? executableNode.name.value : DEFAULT_NAME}'), but it does not exist. Maybe you forgot to import it from another document?`);
97
+ }
98
+ return definition;
99
+ })]));
100
+ }
101
+ const TYPENAME_FIELD = {
102
+ kind: 'Field',
103
+ alias: null,
104
+ name: {
105
+ kind: 'Name',
106
+ value: '__typename'
107
+ }
108
+ };
109
+ function addTypename(definition) {
110
+ for (const {
111
+ selections
112
+ } of selectionSetsForDefinition(definition)) {
113
+ const hasTypename = selections.some(selection => selection.kind === 'Field' && selection.name.value === '__typename');
114
+ if (!hasTypename) {
115
+ selections.push(TYPENAME_FIELD);
116
+ }
117
+ }
118
+ }
119
+ function collectUsedFragmentSpreads(definition, usedSpreads) {
120
+ for (const selection of selectionsForDefinition(definition)) {
121
+ if (selection.kind === 'FragmentSpread') {
122
+ usedSpreads.add(selection.name.value);
123
+ }
124
+ }
125
+ return usedSpreads;
126
+ }
127
+ function selectionsForDefinition(definition) {
128
+ if (!('selectionSet' in definition) || definition.selectionSet == null) {
129
+ return [][Symbol.iterator]();
130
+ }
131
+ return selectionsForSelectionSet(definition.selectionSet);
132
+ }
133
+ function* selectionSetsForDefinition(definition) {
134
+ if (!('selectionSet' in definition) || definition.selectionSet == null) {
135
+ return [][Symbol.iterator]();
136
+ }
137
+ if (definition.kind !== 'OperationDefinition') {
138
+ yield definition.selectionSet;
139
+ }
140
+ for (const nestedSelection of selectionsForDefinition(definition)) {
141
+ if ('selectionSet' in nestedSelection && nestedSelection.selectionSet != null) {
142
+ yield nestedSelection.selectionSet;
143
+ }
144
+ }
145
+ }
146
+ function* selectionsForSelectionSet({
147
+ selections
148
+ }) {
149
+ for (const selection of selections) {
150
+ yield selection;
151
+ if ('selectionSet' in selection && selection.selectionSet != null) {
152
+ yield* selectionsForSelectionSet(selection.selectionSet);
153
+ }
154
+ }
155
+ }
156
+ function stripDocumentLoc(loc) {
157
+ const normalizedLoc = {
158
+ ...loc
159
+ };
160
+ delete normalizedLoc.endToken;
161
+ delete normalizedLoc.startToken;
162
+ return normalizedLoc;
163
+ }
164
+ function stripLoc(value) {
165
+ if (Array.isArray(value)) {
166
+ value.forEach(stripLoc);
167
+ } else if (typeof value === 'object') {
168
+ if (value == null) {
169
+ return;
170
+ }
171
+ if ('loc' in value) {
172
+ delete value.loc;
173
+ }
174
+ for (const key of Object.keys(value)) {
175
+ stripLoc(value[key]);
176
+ }
177
+ }
178
+ }
179
+ function minifyGraphQLSource(source) {
180
+ return source.replace(/#.*/g, '').replace(/\\n/g, ' ').replace(/\s\s+/g, ' ').replace(/\s*({|}|\(|\)|\.|:|,)\s*/g, '$1');
181
+ }
182
+
183
+ exports.cleanGraphQLDocument = cleanGraphQLDocument;
184
+ exports.extractGraphQLImports = extractGraphQLImports;
185
+ exports.minifyGraphQLSource = minifyGraphQLSource;
186
+ exports.toGraphQLOperation = toGraphQLOperation;
@@ -0,0 +1,86 @@
1
+ 'use strict';
2
+
3
+ var path = require('node:path');
4
+ var promises = require('node:fs/promises');
5
+ var graphql$1 = require('graphql');
6
+ var transform = require('./graphql/transform.cjs');
7
+
8
+ function graphql({
9
+ manifest
10
+ } = {}) {
11
+ const shouldWriteManifest = Boolean(manifest);
12
+ const manifestPath = typeof manifest === 'string' ? manifest : `manifests/graphql.json`;
13
+ return {
14
+ name: '@quilted/graphql',
15
+ async transform(code, id) {
16
+ if (!id.endsWith('.graphql') && !id.endsWith('.gql')) return null;
17
+ const topLevelDefinitions = new Set();
18
+ const loadedDocument = await loadDocument(code, id, this, (document, level) => {
19
+ if (level !== 0) return;
20
+ for (const definition of document.definitions) {
21
+ if ('name' in definition && definition.name != null) {
22
+ topLevelDefinitions.add(definition.name.value);
23
+ }
24
+ }
25
+ });
26
+ const document = transform.toGraphQLOperation(transform.cleanGraphQLDocument(loadedDocument, {
27
+ removeUnused: {
28
+ exclude: topLevelDefinitions
29
+ }
30
+ }));
31
+ return {
32
+ code: `export default JSON.parse(${JSON.stringify(JSON.stringify(document))})`,
33
+ meta: shouldWriteManifest ? {
34
+ quilt: {
35
+ graphql: document
36
+ }
37
+ } : undefined
38
+ };
39
+ },
40
+ async generateBundle() {
41
+ if (!shouldWriteManifest) return;
42
+ const operations = {};
43
+ for (const moduleId of this.getModuleIds()) {
44
+ const operation = this.getModuleInfo(moduleId)?.meta?.quilt?.graphql;
45
+ if (operation != null && typeof operation.id === 'string' && typeof operation.source === 'string') {
46
+ operations[operation.id] = operation.source;
47
+ }
48
+ }
49
+ await promises.mkdir(path.dirname(manifestPath), {
50
+ recursive: true
51
+ });
52
+ await promises.writeFile(manifestPath, JSON.stringify(operations, null, 2));
53
+ }
54
+ };
55
+ }
56
+ async function loadDocument(code, file, plugin, add, level = 0, seen = new Set()) {
57
+ const {
58
+ imports,
59
+ source
60
+ } = transform.extractGraphQLImports(code);
61
+ const document = graphql$1.parse(source);
62
+ add?.(document, level);
63
+ if (imports.length === 0) {
64
+ return document;
65
+ }
66
+ const resolvedImports = await Promise.all(imports.map(async imported => {
67
+ if (seen.has(imported)) return;
68
+ seen.add(imported);
69
+ const resolvedId = await plugin.resolve(imported, file);
70
+ if (resolvedId == null) {
71
+ throw new Error(`Could not find ${JSON.stringify(imported)} from ${JSON.stringify(file)}`);
72
+ }
73
+ plugin.addWatchFile(resolvedId.id);
74
+ const contents = await promises.readFile(resolvedId.id, {
75
+ encoding: 'utf8'
76
+ });
77
+ return loadDocument(contents, resolvedId.id, plugin, add, level + 1, seen);
78
+ }));
79
+ for (const importedDocument of resolvedImports) {
80
+ if (importedDocument == null) continue;
81
+ document.definitions.push(...importedDocument.definitions);
82
+ }
83
+ return document;
84
+ }
85
+
86
+ exports.graphql = graphql;
@@ -10,7 +10,6 @@ exports.magicModuleEnv = env.magicModuleEnv;
10
10
  exports.magicModuleAppBrowserEntry = app.magicModuleAppBrowserEntry;
11
11
  exports.magicModuleAppComponent = app.magicModuleAppComponent;
12
12
  exports.magicModuleAppRequestRouter = app.magicModuleAppRequestRouter;
13
- exports.quiltApp = app.quiltApp;
14
13
  exports.quiltAppBrowser = app.quiltAppBrowser;
15
14
  exports.quiltAppServer = app.quiltAppServer;
16
15
  exports.magicModuleRequestRouterEntry = requestRouter.magicModuleRequestRouterEntry;
@@ -11,5 +11,27 @@ function smartReplace(values, options) {
11
11
  values
12
12
  });
13
13
  }
14
+ async function getNodePlugins() {
15
+ const [{
16
+ default: commonjs
17
+ }, {
18
+ default: json
19
+ }, {
20
+ default: nodeResolve
21
+ }, {
22
+ default: nodeExternals
23
+ }] = await Promise.all([import('@rollup/plugin-commonjs'), import('@rollup/plugin-json'), import('@rollup/plugin-node-resolve'), import('rollup-plugin-node-externals')]);
24
+ return [nodeExternals({}), nodeResolve({
25
+ preferBuiltins: true,
26
+ dedupe: []
27
+ // extensions,
28
+ // exportConditions,
29
+ }), commonjs(), json()];
30
+ }
31
+ function rollupPluginsToArray(plugins) {
32
+ return Array.isArray(plugins) ? [...plugins] : plugins ? [plugins] : [];
33
+ }
14
34
 
35
+ exports.getNodePlugins = getNodePlugins;
36
+ exports.rollupPluginsToArray = rollupPluginsToArray;
15
37
  exports.smartReplace = smartReplace;