@siddharatha/adapter-node-rolldown 1.0.9 → 1.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/index.js CHANGED
@@ -1,38 +1,30 @@
1
- import { writeFileSync, readFileSync } from 'node:fs';
1
+ import { readFileSync, writeFileSync } from 'node:fs';
2
2
  import { fileURLToPath } from 'node:url';
3
3
  import { rolldown } from 'rolldown';
4
4
 
5
- const files = fileURLToPath(new URL('./files', import.meta.url).href);
6
5
 
7
6
  /**
8
- * @typedef {Object} AdapterOptions
9
- * @property {string} [out='build'] - Output directory
10
- * @property {boolean} [precompress=true] - Pre-compress static assets
11
- * @property {string} [envPrefix=''] - Prefix for environment variables
12
- * @property {boolean} [polyfill=true] - Inject global polyfills
13
- * @property {string[]|((pkg: any) => string[])} [external] - External packages to exclude from bundle
14
- * @property {object} [rolldownOptions={}] - Additional rolldown configuration options
7
+ * @template T
8
+ * @template {keyof T} K
9
+ * @typedef {Partial<Omit<T, K>> & Required<Pick<T, K>>} PartialExcept
15
10
  */
16
11
 
17
12
  /**
18
- * High-performance SvelteKit adapter for Node.js
19
- * @param {AdapterOptions} options
13
+ * We use a custom `Builder` type here to support the minimum version of SvelteKit.
14
+ * @typedef {PartialExcept<import('@sveltejs/kit').Builder, 'log' | 'rimraf' | 'mkdirp' | 'config' | 'prerendered' | 'routes' | 'createEntries' | 'findServerAssets' | 'generateFallback' | 'generateEnvModule' | 'generateManifest' | 'getBuildDirectory' | 'getClientDirectory' | 'getServerDirectory' | 'getAppPath' | 'writeClient' | 'writePrerendered' | 'writePrerendered' | 'writeServer' | 'copy' | 'compress'>} Builder2_4_0
20
15
  */
21
- export default function (options = {}) {
22
- const {
23
- out = 'build',
24
- precompress = true,
25
- envPrefix = '',
26
- polyfill = true,
27
- external,
28
- rolldownOptions = {}
29
- } = options;
30
16
 
31
- return {
32
- name: '@sveltejs/adapter-node-rolldown',
17
+ const files = fileURLToPath(new URL('./files', import.meta.url).href);
33
18
 
19
+ /** @type {import('./index.js').default} */
20
+ export default function (opts = {}) {
21
+ const { out = 'build', precompress = true, envPrefix = '', external, bundleAll = false, rolldownOptions = {} } = opts;
22
+
23
+ return {
24
+ name: '@sveltejs/adapter-node',
25
+ /** @param {Builder2_4_0} builder */
34
26
  async adapt(builder) {
35
- const tmp = builder.getBuildDirectory('adapter-node-rolldown');
27
+ const tmp = builder.getBuildDirectory('adapter-node');
36
28
 
37
29
  builder.rimraf(out);
38
30
  builder.rimraf(tmp);
@@ -65,72 +57,29 @@ export default function (options = {}) {
65
57
 
66
58
  const pkg = JSON.parse(readFileSync('package.json', 'utf8'));
67
59
 
60
+ // determine external patterns for bundling
61
+ let externalPatterns;
62
+ if (bundleAll) {
63
+ externalPatterns = [];
64
+ } else if (external) {
65
+ externalPatterns = typeof external === 'function' ? external(pkg) : external;
66
+ } else {
67
+ externalPatterns = Object.keys(pkg.dependencies || {});
68
+ }
69
+
68
70
  /** @type {Record<string, string>} */
69
71
  const input = {
70
72
  index: `${tmp}/index.js`,
71
73
  manifest: `${tmp}/manifest.js`
72
74
  };
73
75
 
74
- // Support for instrumentation files
75
76
  if (builder.hasServerInstrumentationFile?.()) {
76
77
  input['instrumentation.server'] = `${tmp}/instrumentation.server.js`;
77
78
  }
78
79
 
79
- // Node.js built-in modules (always external)
80
- const builtins = [
81
- 'assert', 'async_hooks', 'buffer', 'child_process', 'cluster', 'console',
82
- 'constants', 'crypto', 'dgram', 'diagnostics_channel', 'dns', 'domain',
83
- 'events', 'fs', 'http', 'http2', 'https', 'inspector', 'module', 'net',
84
- 'os', 'path', 'perf_hooks', 'process', 'punycode', 'querystring', 'readline',
85
- 'repl', 'stream', 'string_decoder', 'sys', 'timers', 'tls', 'trace_events',
86
- 'tty', 'url', 'util', 'v8', 'vm', 'wasi', 'worker_threads', 'zlib'
87
- ];
88
-
89
- // Determine external packages
90
- // Always include runtime dependencies and user dependencies
91
- const runtimeDeps = ['polka', 'sirv', '@polka/url'];
92
-
93
- // OpenTelemetry packages have CommonJS/require issues when bundled as ESM
94
- // Always mark them as external
95
- const otelPackages = [
96
- '@opentelemetry/api',
97
- '@opentelemetry/sdk-node',
98
- '@opentelemetry/auto-instrumentations-node',
99
- '@opentelemetry/exporter-trace-otlp-http',
100
- '@opentelemetry/exporter-trace-otlp-grpc',
101
- '@opentelemetry/exporter-metrics-otlp-http',
102
- '@opentelemetry/exporter-metrics-otlp-grpc',
103
- '@opentelemetry/otlp-exporter-base',
104
- '@opentelemetry/resources',
105
- '@opentelemetry/semantic-conventions',
106
- '@opentelemetry/core',
107
- '@opentelemetry/instrumentation',
108
- 'import-in-the-middle'
109
- ];
110
-
111
- let externalPackages = [...runtimeDeps, ...otelPackages];
112
-
113
- if (typeof external === 'function') {
114
- externalPackages = [...externalPackages, ...external(pkg)];
115
- } else if (Array.isArray(external)) {
116
- externalPackages = [...externalPackages, ...external];
117
- } else {
118
- // Default: use package.json dependencies
119
- externalPackages = [...externalPackages, ...Object.keys(pkg.dependencies || {})];
120
- }
121
-
122
- // Combine builtins with external packages
123
- const allExternal = [...new Set([...builtins, ...externalPackages])];
124
-
125
- // Convert to regex patterns for deep exports support
126
- // Also handle node: prefix for built-ins
127
- const externalPatterns = allExternal.map((d) =>
128
- new RegExp(`^(node:)?${d.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}(\\/.*)?$`)
129
- );
130
-
131
- // Bundle the Vite output so deployments only need production dependencies
132
- // Anything in devDependencies will get included in the bundled code
133
- // Rolldown has native support for node resolution, CommonJS, and JSON
80
+ // we bundle the Vite output so that deployments only need
81
+ // their production dependencies. Anything in devDependencies
82
+ // will get included in the bundled code
134
83
  const bundle = await rolldown({
135
84
  input,
136
85
  external: externalPatterns,
@@ -147,59 +96,21 @@ export default function (options = {}) {
147
96
  dir: `${out}/server`,
148
97
  format: 'esm',
149
98
  sourcemap: true,
150
- chunkFileNames: 'chunks/[name]-[hash].js',
151
- minify: false,
152
- keepNames: true
99
+ chunkFileNames: 'chunks/[name]-[hash].js'
153
100
  });
154
101
 
155
- // Now copy and bundle the runtime files
156
- builder.copy(files, `${tmp}/runtime`, {
102
+ builder.copy(files, out, {
157
103
  replace: {
104
+ ENV: './env.js',
158
105
  HANDLER: './handler.js',
159
106
  MANIFEST: './server/manifest.js',
160
107
  SERVER: './server/index.js',
161
108
  SHIMS: './shims.js',
162
- MIDDLEWARES: './middlewares.js',
163
109
  ENV_PREFIX: JSON.stringify(envPrefix),
164
- POLYFILL: JSON.stringify(polyfill)
110
+ PRECOMPRESS: JSON.stringify(precompress)
165
111
  }
166
112
  });
167
113
 
168
- // Bundle the runtime files (second pass)
169
- // Mark the server files as external since they're already bundled
170
- const runtimeExternalPatterns = [
171
- ...externalPatterns,
172
- /^\.\/server\// // Server files are already bundled, keep them external
173
- ];
174
-
175
- const runtimeBundle = await rolldown({
176
- input: {
177
- index: `${tmp}/runtime/index.js`
178
- },
179
- external: runtimeExternalPatterns,
180
- platform: 'node',
181
- resolve: {
182
- conditionNames: ['node', 'import'],
183
- modulePaths: [
184
- fileURLToPath(new URL('./node_modules', import.meta.url)),
185
- ...(rolldownOptions.resolve?.modulePaths || [])
186
- ],
187
- ...rolldownOptions.resolve
188
- },
189
- cwd: process.cwd(),
190
- ...rolldownOptions
191
- });
192
-
193
- await runtimeBundle.write({
194
- dir: `${out}`,
195
- format: 'esm',
196
- sourcemap: true,
197
- chunkFileNames: 'chunks/[name]-[hash].js',
198
- minify: false,
199
- keepNames: true
200
- });
201
-
202
- // Support for instrumentation
203
114
  if (builder.hasServerInstrumentationFile?.()) {
204
115
  builder.instrument?.({
205
116
  entrypoint: `${out}/index.js`,
@@ -209,49 +120,6 @@ export default function (options = {}) {
209
120
  }
210
121
  });
211
122
  }
212
-
213
- builder.log.minor('Generating package.json');
214
-
215
- // Collect all user dependencies (both dependencies and devDependencies)
216
- const allUserDeps = {
217
- ...(pkg.dependencies || {}),
218
- ...(pkg.devDependencies || {})
219
- };
220
-
221
- // Include adapter runtime dependencies + all user dependencies
222
- const finalDeps = {
223
- '@polka/url': '^1.0.0-next.28',
224
- 'polka': '^0.5.2',
225
- 'sirv': '^3.0.2',
226
- ...(pkg.dependencies || {})
227
- };
228
-
229
- // Add OpenTelemetry packages if they're used (from devDependencies or dependencies)
230
- for (const otelPkg of otelPackages) {
231
- if (allUserDeps[otelPkg] && !finalDeps[otelPkg]) {
232
- finalDeps[otelPkg] = allUserDeps[otelPkg];
233
- }
234
- }
235
-
236
- builder.log.info(`Including ${Object.keys(finalDeps).length} dependencies in output package.json`);
237
-
238
- writeFileSync(
239
- `${out}/package.json`,
240
- JSON.stringify(
241
- {
242
- name: pkg.name || 'sveltekit-app',
243
- version: pkg.version || '1.0.0',
244
- type: 'module',
245
- main: './index.js',
246
- dependencies: finalDeps
247
- },
248
- null,
249
- 2
250
- )
251
- );
252
-
253
- builder.log.success(`Adapter complete! Output: ${out}`);
254
- builder.log.info(`\nTo run the server:\n cd ${out}\n npm install\n node index.js\n`);
255
123
  },
256
124
 
257
125
  supports: {
@@ -259,4 +127,4 @@ export default function (options = {}) {
259
127
  instrumentation: () => true
260
128
  }
261
129
  };
262
- }
130
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@siddharatha/adapter-node-rolldown",
3
- "version": "1.0.9",
4
- "description": "High-performance SvelteKit adapter for Node.js with Polka, WebSockets, and OpenTelemetry",
3
+ "version": "1.1.3",
4
+ "description": "Just replacing @sveltejs/adapter-node with rolldown",
5
5
  "type": "module",
6
6
  "exports": {
7
7
  ".": "./index.js"
@@ -12,7 +12,12 @@
12
12
  "index.d.ts"
13
13
  ],
14
14
  "scripts": {
15
- "test": "echo \"Error: no test specified\" && exit 1"
15
+ "dev": "rollup -cw",
16
+ "build": "rollup -c",
17
+ "check": "tsc",
18
+ "lint": "prettier --check .",
19
+ "format": "pnpm lint --write",
20
+ "prepublishOnly": "pnpm build"
16
21
  },
17
22
  "keywords": [
18
23
  "svelte",
@@ -29,25 +34,19 @@
29
34
  "author": "",
30
35
  "license": "MIT",
31
36
  "dependencies": {
32
- "rolldown": "^0.15.0"
37
+ "rolldown": "1.0.0-beta.60",
38
+ "@rollup/plugin-commonjs": "^28.0.1",
39
+ "@rollup/plugin-json": "^6.1.0",
40
+ "@rollup/plugin-node-resolve": "^16.0.0",
41
+ "rollup": "^4.9.5"
33
42
  },
34
43
  "peerDependencies": {
35
44
  "@sveltejs/kit": "^2.4.0"
36
45
  },
37
46
  "devDependencies": {
38
- "@sveltejs/kit": "^2.4.0",
39
47
  "@polka/url": "^1.0.0-next.28",
48
+ "@sveltejs/kit": "^2.4.0",
40
49
  "polka": "^0.5.2",
41
- "sirv": "^3.0.2",
42
- "compression": "^1.7.4",
43
- "ws": "^8.16.0",
44
- "@opentelemetry/sdk-node": "0.48.0",
45
- "@opentelemetry/auto-instrumentations-node": "0.41.0",
46
- "@opentelemetry/exporter-trace-otlp-http": "0.48.0",
47
- "@opentelemetry/exporter-trace-otlp-grpc": "0.48.0",
48
- "@opentelemetry/resources": "1.21.0",
49
- "@opentelemetry/semantic-conventions": "1.21.0",
50
- "@opentelemetry/api": "1.7.0",
51
- "import-in-the-middle": "^2.0.3"
50
+ "sirv": "^3.0.2"
52
51
  }
53
52
  }
@@ -1,162 +0,0 @@
1
- import compression from 'compression';
2
- import sirv from 'sirv';
3
- import { config } from 'ENV';
4
-
5
- /**
6
- * Create compression middleware with optimized settings
7
- */
8
- export function createCompressionMiddleware() {
9
- if (!config.compression) {
10
- return (req, res, next) => next();
11
- }
12
-
13
- return compression({
14
- level: config.compressionLevel,
15
- threshold: 1024, // Only compress responses > 1KB
16
- memLevel: 8,
17
- filter: (req, res) => {
18
- const contentType = res.getHeader('Content-Type');
19
-
20
- // Don't compress images, videos, or already compressed content
21
- if (contentType && (
22
- contentType.includes('image/') ||
23
- contentType.includes('video/') ||
24
- contentType.includes('audio/') ||
25
- contentType.includes('font/')
26
- )) {
27
- return false;
28
- }
29
-
30
- // Use default compression filter for everything else
31
- return compression.filter(req, res);
32
- }
33
- });
34
- }
35
-
36
- /**
37
- * Create static file serving middleware with optimized settings
38
- */
39
- export function createStaticMiddleware(dir, precompressed = true) {
40
- return sirv(dir, {
41
- dev: false,
42
- etag: true,
43
- maxAge: 31536000, // 1 year for immutable assets
44
- immutable: true,
45
- gzip: precompressed, // Serve .gz files if available
46
- brotli: precompressed, // Serve .br files if available
47
- setHeaders: (res, pathname) => {
48
- // Cache control based on file type
49
- if (pathname.includes('immutable')) {
50
- res.setHeader('Cache-Control', 'public, immutable, max-age=31536000');
51
- } else if (pathname.endsWith('.html')) {
52
- res.setHeader('Cache-Control', 'public, max-age=0, must-revalidate');
53
- } else {
54
- res.setHeader('Cache-Control', 'public, max-age=3600');
55
- }
56
- }
57
- });
58
- }
59
-
60
- /**
61
- * Simple body parser for JSON and URL-encoded data with configurable limits
62
- */
63
- export function createBodyParser() {
64
- const limitBytes = parseLimit(config.bodyLimit);
65
-
66
- return (req, res, next) => {
67
- // Skip if not a body method
68
- if (req.method !== 'POST' && req.method !== 'PUT' && req.method !== 'PATCH') {
69
- return next();
70
- }
71
-
72
- // Skip if body already parsed
73
- if (req.body) {
74
- return next();
75
- }
76
-
77
- const contentType = req.headers['content-type'] || '';
78
-
79
- // Skip if not JSON or form data (SvelteKit will handle FormData/multipart)
80
- if (!contentType.includes('application/json') &&
81
- !contentType.includes('application/x-www-form-urlencoded')) {
82
- return next();
83
- }
84
-
85
- let data = '';
86
- let size = 0;
87
-
88
- req.on('data', chunk => {
89
- size += chunk.length;
90
- if (size > limitBytes) {
91
- req.removeAllListeners('data');
92
- req.removeAllListeners('end');
93
- res.writeHead(413, { 'Content-Type': 'application/json' });
94
- res.end(JSON.stringify({ error: 'Request body too large' }));
95
- return;
96
- }
97
- data += chunk.toString();
98
- });
99
-
100
- req.on('end', () => {
101
- try {
102
- if (contentType.includes('application/json')) {
103
- req.body = data ? JSON.parse(data) : {};
104
- } else if (contentType.includes('application/x-www-form-urlencoded')) {
105
- req.body = parseUrlEncoded(data);
106
- }
107
- } catch (error) {
108
- res.writeHead(400, { 'Content-Type': 'application/json' });
109
- res.end(JSON.stringify({ error: 'Invalid request body' }));
110
- return;
111
- }
112
- next();
113
- });
114
-
115
- req.on('error', (error) => {
116
- console.error('Body parse error:', error);
117
- res.writeHead(400, { 'Content-Type': 'application/json' });
118
- res.end(JSON.stringify({ error: 'Request error' }));
119
- });
120
- };
121
- }
122
-
123
- /**
124
- * Parse size limit string to bytes
125
- */
126
- function parseLimit(limit) {
127
- if (typeof limit === 'number') return limit;
128
-
129
- const match = limit.match(/^(\d+(?:\.\d+)?)\s*(kb|mb|gb)?$/i);
130
- if (!match) return 10 * 1024 * 1024; // Default 10MB
131
-
132
- const value = parseFloat(match[1]);
133
- const unit = (match[2] || 'b').toLowerCase();
134
-
135
- const multipliers = {
136
- b: 1,
137
- kb: 1024,
138
- mb: 1024 * 1024,
139
- gb: 1024 * 1024 * 1024
140
- };
141
-
142
- return value * (multipliers[unit] || 1);
143
- }
144
-
145
- /**
146
- * Parse URL-encoded form data
147
- */
148
- function parseUrlEncoded(str) {
149
- const obj = {};
150
- const pairs = str.split('&');
151
-
152
- for (const pair of pairs) {
153
- const [key, value] = pair.split('=');
154
- if (key) {
155
- const decodedKey = decodeURIComponent(key);
156
- const decodedValue = value ? decodeURIComponent(value.replace(/\+/g, ' ')) : '';
157
- obj[decodedKey] = decodedValue;
158
- }
159
- }
160
-
161
- return obj;
162
- }
@@ -1,126 +0,0 @@
1
- import { config } from 'ENV';
2
-
3
- let sdk = null;
4
-
5
- /**
6
- * Initialize OpenTelemetry SDK for distributed tracing
7
- */
8
- export async function initTelemetry() {
9
- if (!config.telemetry.enabled) {
10
- console.log('OpenTelemetry is disabled');
11
- return null;
12
- }
13
-
14
- try {
15
- // Dynamic imports to avoid loading if telemetry is disabled
16
- const { NodeSDK } = await import('@opentelemetry/sdk-node');
17
- const { getNodeAutoInstrumentations } = await import('@opentelemetry/auto-instrumentations-node');
18
- const { Resource } = await import('@opentelemetry/resources');
19
- const { SemanticResourceAttributes } = await import('@opentelemetry/semantic-conventions');
20
-
21
- // Choose exporter based on protocol
22
- let TraceExporter;
23
- if (config.telemetry.protocol === 'grpc') {
24
- const module = await import('@opentelemetry/exporter-trace-otlp-grpc');
25
- TraceExporter = module.OTLPTraceExporter;
26
- } else {
27
- const module = await import('@opentelemetry/exporter-trace-otlp-http');
28
- TraceExporter = module.OTLPTraceExporter;
29
- }
30
-
31
- const exporterConfig = {
32
- url: config.telemetry.endpoint,
33
- headers: {}
34
- };
35
-
36
- // Add Dynatrace API token if provided
37
- if (config.telemetry.dynatraceToken) {
38
- exporterConfig.headers['Authorization'] = `Api-Token ${config.telemetry.dynatraceToken}`;
39
- }
40
-
41
- // Merge custom headers from config
42
- if (config.telemetry.customConfig.headers) {
43
- Object.assign(exporterConfig.headers, config.telemetry.customConfig.headers);
44
- }
45
-
46
- sdk = new NodeSDK({
47
- resource: new Resource({
48
- [SemanticResourceAttributes.SERVICE_NAME]: config.telemetry.serviceName,
49
- [SemanticResourceAttributes.SERVICE_VERSION]: config.telemetry.serviceVersion,
50
- ...config.telemetry.customConfig.resourceAttributes
51
- }),
52
- traceExporter: new TraceExporter(exporterConfig),
53
- instrumentations: [
54
- getNodeAutoInstrumentations({
55
- // Disable noisy instrumentations
56
- '@opentelemetry/instrumentation-fs': {
57
- enabled: false
58
- },
59
- '@opentelemetry/instrumentation-http': {
60
- enabled: true,
61
- ignoreIncomingPaths: ['/health', '/readiness'],
62
- ignoreOutgoingUrls: [/\/health$/, /\/readiness$/]
63
- },
64
- '@opentelemetry/instrumentation-dns': {
65
- enabled: false
66
- },
67
- // Enable all others by default
68
- ...config.telemetry.customConfig.instrumentations
69
- })
70
- ],
71
- // Sampling configuration
72
- ...(config.telemetry.sampleRate < 1.0 && {
73
- sampler: {
74
- shouldSample: () => {
75
- return Math.random() < config.telemetry.sampleRate
76
- ? { decision: 1 } // RECORD_AND_SAMPLED
77
- : { decision: 0 }; // NOT_RECORD
78
- }
79
- }
80
- })
81
- });
82
-
83
- await sdk.start();
84
- console.log('OpenTelemetry SDK initialized successfully');
85
- console.log(` Service: ${config.telemetry.serviceName}`);
86
- console.log(` Endpoint: ${config.telemetry.endpoint || 'default'}`);
87
- console.log(` Protocol: ${config.telemetry.protocol}`);
88
- console.log(` Sample Rate: ${(config.telemetry.sampleRate * 100).toFixed(1)}%`);
89
-
90
- return sdk;
91
- } catch (error) {
92
- console.error('Failed to initialize OpenTelemetry:', error.message);
93
- return null;
94
- }
95
- }
96
-
97
- /**
98
- * Shutdown telemetry SDK and flush remaining spans
99
- */
100
- export async function shutdownTelemetry() {
101
- if (sdk) {
102
- console.log('Shutting down OpenTelemetry SDK...');
103
- try {
104
- await sdk.shutdown();
105
- console.log('OpenTelemetry SDK shutdown complete');
106
- } catch (error) {
107
- console.error('Error shutting down OpenTelemetry:', error.message);
108
- }
109
- }
110
- }
111
-
112
- /**
113
- * Get tracer for custom instrumentation
114
- */
115
- export function getTracer(name = 'sveltekit-adapter') {
116
- if (!config.telemetry.enabled) {
117
- return null;
118
- }
119
-
120
- try {
121
- const { trace } = require('@opentelemetry/api');
122
- return trace.getTracer(name);
123
- } catch {
124
- return null;
125
- }
126
- }