svelte-bundle 0.0.12 → 0.0.22
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/ReadMe.md +18 -20
- package/bundle.js +120 -33
- package/cli.js +49 -25
- package/package.json +12 -3
package/ReadMe.md
CHANGED
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
> ⚠️ **NOTICE:** While this tool is currently functional, it has not nearly been battle-tested enough to ensure it works in most use-cases.
|
|
2
|
+
|
|
3
|
+
## Usage
|
|
4
|
+
This tool can be used with `npx`:
|
|
5
|
+
```bash
|
|
6
|
+
npx svelte-bundle -i <input-dir> -o <output-dir>
|
|
7
|
+
```
|
|
8
|
+
### Full Documentation: [https://github.com/uhteddy/svelte-bundle/wiki](https://github.com/uhteddy/svelte-bundle/wiki)
|
|
9
|
+
|
|
1
10
|
# Svelte Bundle CLI
|
|
2
11
|
|
|
3
12
|
**Svelte Bundle CLI** is a simple command-line tool that allows you to bundle a Svelte application into a single `.html` file using Vite and SSR (Server-Side Rendering). The goal of this tool is to make it easy to bundle Svelte apps for deployment, particularly for cases where everything needs to be contained in a single file.
|
|
@@ -6,9 +15,7 @@ Just note, the purpose of this tool is **NOT** to bundle an entire Svelte app, i
|
|
|
6
15
|
|
|
7
16
|
Utilizing this you will be able to develop a page with the joy of svelte and be able to directly get a single `.html` file that you can utilize getting full use-case of svelte.
|
|
8
17
|
|
|
9
|
-
⚠️ **Note**: This tool is **NOT** made to function with SvelteKit, this takes a single `.svelte` file as input and
|
|
10
|
-
|
|
11
|
-
⚠️ **Note**: This tool is currently in early development and is not fully complete. The roadmap and additional features will be added over time. There is currently **NO** testing on this, meaning there is no guarentee it will work for most usecases.
|
|
18
|
+
⚠️ **Note**: This tool is **NOT** made to function with SvelteKit natively, this takes a single `.svelte` file as input and will output a single `.html` file.
|
|
12
19
|
|
|
13
20
|
## Inspiration
|
|
14
21
|
This tool was inspired by the need I had when it came to updating the CMS for a company I worked for. They were looking for more custom content on their website which used an outdated CMS. Pages were only able to include HTML, CSS, and JS.
|
|
@@ -19,20 +26,11 @@ So, I searched for tools around that could be of assistance. I found [figsvelte]
|
|
|
19
26
|
|
|
20
27
|
I noticed through a lot of google searches I wasn't the only one looking for a solution like this, yet, I was unable to find one that addressed everything I was looking for. So, for this reason I have decided to build svelte-bundle to take care of this in a much more simplistic and CLI way.
|
|
21
28
|
|
|
22
|
-
## Features
|
|
23
|
-
- [
|
|
24
|
-
- [
|
|
25
|
-
- [
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
- [
|
|
29
|
-
- [
|
|
30
|
-
- [ ] Implement error handling and more robust validation.
|
|
31
|
-
- [ ] Add support for environment-specific builds (development/production).
|
|
32
|
-
- [ ] Documentation and guides on using the tool with different Svelte apps.
|
|
33
|
-
- [ ] Tests and CI integration.
|
|
34
|
-
|
|
35
|
-
## Installation (Planned)
|
|
36
|
-
Once the tool is published on npm, it will be available via `npx`:
|
|
37
|
-
```bash
|
|
38
|
-
npx svelte-bundle -i <input-dir> -o <output-dir>
|
|
29
|
+
## Features
|
|
30
|
+
- [x] Bundles Svelte applications
|
|
31
|
+
- [x] Outputs a single `.html` file ready for deployment.
|
|
32
|
+
- [x] CLI arguments for specifying input and output directories.
|
|
33
|
+
- [x] Tests and CI integration.
|
|
34
|
+
- [x] Handle CSS and assets within the bundled file.
|
|
35
|
+
- [x] Add tailwindcss support, **this is in beta**.
|
|
36
|
+
- [x] Implement error handling and more robust validation.
|
package/bundle.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
// bundle.js
|
|
2
1
|
import fs from 'fs/promises';
|
|
3
2
|
import path from 'path';
|
|
4
3
|
import { rollup } from 'rollup';
|
|
@@ -7,11 +6,76 @@ import resolve from '@rollup/plugin-node-resolve';
|
|
|
7
6
|
import commonjs from '@rollup/plugin-commonjs';
|
|
8
7
|
import css from 'rollup-plugin-css-only';
|
|
9
8
|
import terser from '@rollup/plugin-terser';
|
|
9
|
+
import postcss from 'postcss';
|
|
10
|
+
import tailwindcss from 'tailwindcss';
|
|
11
|
+
import autoprefixer from 'autoprefixer';
|
|
12
|
+
import cssnano from 'cssnano';
|
|
13
|
+
import { createRequire } from 'module';
|
|
14
|
+
import { fileURLToPath } from 'url';
|
|
15
|
+
|
|
16
|
+
const require = createRequire(import.meta.url);
|
|
17
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
18
|
+
const __dirname = path.dirname(__filename);
|
|
19
|
+
|
|
20
|
+
// Function to find the CLI's node_modules directory
|
|
21
|
+
const findCliNodeModules = () => {
|
|
22
|
+
return path.resolve(__dirname, 'node_modules');
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export async function buildStaticFile(svelteFilePath, outputDir, options = {}) {
|
|
26
|
+
const { useTailwind = false, tailwindConfig = null } = options;
|
|
10
27
|
|
|
11
|
-
export async function buildStaticFile(svelteFilePath, outputDir) {
|
|
12
28
|
try {
|
|
29
|
+
// Ensure output directory exists
|
|
30
|
+
await fs.mkdir(outputDir, { recursive: true });
|
|
31
|
+
|
|
32
|
+
// Get CLI's node_modules path
|
|
33
|
+
const cliNodeModules = findCliNodeModules();
|
|
34
|
+
|
|
13
35
|
let cssText = '';
|
|
14
36
|
|
|
37
|
+
// Setup PostCSS plugins based on whether Tailwind is enabled
|
|
38
|
+
const postcssPlugins = useTailwind
|
|
39
|
+
? [
|
|
40
|
+
tailwindcss(tailwindConfig || {
|
|
41
|
+
content: [svelteFilePath],
|
|
42
|
+
theme: { extend: {} },
|
|
43
|
+
plugins: [],
|
|
44
|
+
}),
|
|
45
|
+
autoprefixer(),
|
|
46
|
+
cssnano({
|
|
47
|
+
preset: ['default', {
|
|
48
|
+
discardComments: {
|
|
49
|
+
removeAll: true,
|
|
50
|
+
},
|
|
51
|
+
}],
|
|
52
|
+
})
|
|
53
|
+
]
|
|
54
|
+
: [
|
|
55
|
+
autoprefixer(),
|
|
56
|
+
cssnano({
|
|
57
|
+
preset: ['default', {
|
|
58
|
+
discardComments: {
|
|
59
|
+
removeAll: true,
|
|
60
|
+
},
|
|
61
|
+
}],
|
|
62
|
+
})
|
|
63
|
+
];
|
|
64
|
+
|
|
65
|
+
// Process global styles
|
|
66
|
+
let globalCssText = '';
|
|
67
|
+
if (useTailwind) {
|
|
68
|
+
const tailwindCss = `
|
|
69
|
+
@tailwind base;
|
|
70
|
+
@tailwind components;
|
|
71
|
+
@tailwind utilities;
|
|
72
|
+
`;
|
|
73
|
+
|
|
74
|
+
const processedCss = await postcss(postcssPlugins)
|
|
75
|
+
.process(tailwindCss, { from: undefined });
|
|
76
|
+
globalCssText = processedCss.css;
|
|
77
|
+
}
|
|
78
|
+
|
|
15
79
|
// Create temporary SSR bundle
|
|
16
80
|
const ssrBundle = await rollup({
|
|
17
81
|
input: svelteFilePath,
|
|
@@ -22,7 +86,15 @@ export async function buildStaticFile(svelteFilePath, outputDir) {
|
|
|
22
86
|
hydratable: true,
|
|
23
87
|
css: false
|
|
24
88
|
},
|
|
25
|
-
emitCss: true
|
|
89
|
+
emitCss: true,
|
|
90
|
+
preprocess: useTailwind ? {
|
|
91
|
+
style: async ({ content }) => {
|
|
92
|
+
if (!content) return { code: '' };
|
|
93
|
+
const result = await postcss(postcssPlugins)
|
|
94
|
+
.process(content, { from: undefined });
|
|
95
|
+
return { code: result.css };
|
|
96
|
+
}
|
|
97
|
+
} : undefined
|
|
26
98
|
}),
|
|
27
99
|
css({
|
|
28
100
|
output: function(styles) {
|
|
@@ -31,17 +103,43 @@ export async function buildStaticFile(svelteFilePath, outputDir) {
|
|
|
31
103
|
}),
|
|
32
104
|
resolve({
|
|
33
105
|
browser: true,
|
|
34
|
-
dedupe: ['svelte']
|
|
106
|
+
dedupe: ['svelte'],
|
|
107
|
+
preferBuiltins: false,
|
|
108
|
+
moduleDirectories: ['node_modules'],
|
|
109
|
+
modulePaths: [cliNodeModules],
|
|
110
|
+
rootDir: cliNodeModules
|
|
35
111
|
}),
|
|
36
112
|
commonjs()
|
|
37
|
-
]
|
|
38
|
-
external: ['svelte/internal']
|
|
113
|
+
]
|
|
39
114
|
});
|
|
40
115
|
|
|
41
116
|
// Create a temporary directory for SSR
|
|
42
117
|
const tempDir = path.join(path.dirname(svelteFilePath), '.temp');
|
|
43
118
|
await fs.mkdir(tempDir, { recursive: true });
|
|
44
|
-
|
|
119
|
+
|
|
120
|
+
// Create package.json for the temp directory
|
|
121
|
+
await fs.writeFile(
|
|
122
|
+
path.join(tempDir, 'package.json'),
|
|
123
|
+
JSON.stringify({
|
|
124
|
+
type: 'module',
|
|
125
|
+
dependencies: {
|
|
126
|
+
svelte: require(path.join(cliNodeModules, 'svelte', 'package.json')).version
|
|
127
|
+
}
|
|
128
|
+
}),
|
|
129
|
+
'utf-8'
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
// Create symlink to CLI's node_modules
|
|
133
|
+
const tempNodeModules = path.join(tempDir, 'node_modules');
|
|
134
|
+
try {
|
|
135
|
+
await fs.symlink(cliNodeModules, tempNodeModules, 'junction');
|
|
136
|
+
} catch (error) {
|
|
137
|
+
if (error.code !== 'EEXIST') {
|
|
138
|
+
throw error;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const tempSSRFile = path.join(tempDir, 'ssr-bundle.mjs');
|
|
45
143
|
|
|
46
144
|
// Generate SSR bundle
|
|
47
145
|
await ssrBundle.write({
|
|
@@ -51,7 +149,7 @@ export async function buildStaticFile(svelteFilePath, outputDir) {
|
|
|
51
149
|
});
|
|
52
150
|
|
|
53
151
|
// Import the SSR bundle
|
|
54
|
-
const { default: App } = await import(tempSSRFile);
|
|
152
|
+
const { default: App } = await import(`file://${tempSSRFile}`);
|
|
55
153
|
const { html: initialHtml } = App.render();
|
|
56
154
|
|
|
57
155
|
// Clean up temp files
|
|
@@ -66,7 +164,15 @@ export async function buildStaticFile(svelteFilePath, outputDir) {
|
|
|
66
164
|
hydratable: true,
|
|
67
165
|
css: false
|
|
68
166
|
},
|
|
69
|
-
emitCss: true
|
|
167
|
+
emitCss: true,
|
|
168
|
+
preprocess: useTailwind ? {
|
|
169
|
+
style: async ({ content }) => {
|
|
170
|
+
if (!content) return { code: '' };
|
|
171
|
+
const result = await postcss(postcssPlugins)
|
|
172
|
+
.process(content, { from: undefined });
|
|
173
|
+
return { code: result.css };
|
|
174
|
+
}
|
|
175
|
+
} : undefined
|
|
70
176
|
}),
|
|
71
177
|
css({
|
|
72
178
|
output: function(styles) {
|
|
@@ -75,7 +181,10 @@ export async function buildStaticFile(svelteFilePath, outputDir) {
|
|
|
75
181
|
}),
|
|
76
182
|
resolve({
|
|
77
183
|
browser: true,
|
|
78
|
-
dedupe: ['svelte']
|
|
184
|
+
dedupe: ['svelte'],
|
|
185
|
+
moduleDirectories: ['node_modules'],
|
|
186
|
+
modulePaths: [cliNodeModules],
|
|
187
|
+
rootDir: cliNodeModules
|
|
79
188
|
}),
|
|
80
189
|
commonjs(),
|
|
81
190
|
terser()
|
|
@@ -91,32 +200,10 @@ export async function buildStaticFile(svelteFilePath, outputDir) {
|
|
|
91
200
|
});
|
|
92
201
|
|
|
93
202
|
// Create the final HTML
|
|
94
|
-
const finalHtml = `<!DOCTYPE html>
|
|
95
|
-
<html lang="en">
|
|
96
|
-
<head>
|
|
97
|
-
<meta charset="UTF-8">
|
|
98
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
99
|
-
<title>Static Svelte App</title>
|
|
100
|
-
<style>
|
|
101
|
-
${cssText}
|
|
102
|
-
</style>
|
|
103
|
-
</head>
|
|
104
|
-
<body>
|
|
105
|
-
<div id="app">${initialHtml}</div>
|
|
106
|
-
<script src="https://unpkg.com/svelte@3.58.0/internal/index.js"></script>
|
|
107
|
-
<script>
|
|
108
|
-
${clientCode}
|
|
109
|
-
const app = new App({
|
|
110
|
-
target: document.getElementById('app'),
|
|
111
|
-
hydrate: true
|
|
112
|
-
});
|
|
113
|
-
</script>
|
|
114
|
-
</body>
|
|
115
|
-
</html>`;
|
|
203
|
+
const finalHtml = `<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Static Svelte App</title><style>${globalCssText}${cssText}</style></head><body><div id="app">${initialHtml}</div><script src="https://unpkg.com/svelte@3.58.0/internal/index.js"></script><script>${clientCode}const app=new App({target:document.getElementById("app"),hydrate:!0});</script></body></html>`;
|
|
116
204
|
|
|
117
205
|
// Write the output file
|
|
118
206
|
const outputPath = path.join(outputDir, 'output.html');
|
|
119
|
-
await fs.mkdir(outputDir, { recursive: true });
|
|
120
207
|
await fs.writeFile(outputPath, finalHtml, 'utf-8');
|
|
121
208
|
} catch (error) {
|
|
122
209
|
console.error('Build error:', error);
|
package/cli.js
CHANGED
|
@@ -19,13 +19,11 @@ const packageJson = JSON.parse(
|
|
|
19
19
|
)
|
|
20
20
|
);
|
|
21
21
|
|
|
22
|
-
// Create readline interface for prompts
|
|
23
22
|
const rl = createInterface({
|
|
24
23
|
input: process.stdin,
|
|
25
24
|
output: process.stdout
|
|
26
25
|
});
|
|
27
26
|
|
|
28
|
-
// Promisify readline question
|
|
29
27
|
const question = (query) => new Promise((resolve) => rl.question(query, resolve));
|
|
30
28
|
|
|
31
29
|
program
|
|
@@ -34,28 +32,25 @@ program
|
|
|
34
32
|
.version(packageJson.version)
|
|
35
33
|
.requiredOption('-i, --input <path>', 'Input Svelte file')
|
|
36
34
|
.option('-o, --output <path>', 'Output directory (defaults to current directory)')
|
|
35
|
+
.option('--tw', 'Enable Tailwind CSS processing')
|
|
36
|
+
.option('--tw-config <path>', 'Path to custom Tailwind config file')
|
|
37
37
|
.option('-f, --force', 'Force overwrite without asking');
|
|
38
38
|
|
|
39
39
|
program.parse();
|
|
40
40
|
|
|
41
41
|
const options = program.opts();
|
|
42
42
|
|
|
43
|
-
async function
|
|
43
|
+
async function loadTailwindConfig(configPath) {
|
|
44
|
+
const fullPath = path.resolve(configPath);
|
|
44
45
|
try {
|
|
45
|
-
await
|
|
46
|
-
return
|
|
47
|
-
} catch {
|
|
48
|
-
|
|
46
|
+
const { default: config } = await import(fullPath);
|
|
47
|
+
return config;
|
|
48
|
+
} catch (error) {
|
|
49
|
+
console.error(chalk.red(`Error loading Tailwind config: ${error.message}`));
|
|
50
|
+
process.exit(1);
|
|
49
51
|
}
|
|
50
52
|
}
|
|
51
53
|
|
|
52
|
-
async function shouldOverwrite(filepath) {
|
|
53
|
-
const answer = await question(
|
|
54
|
-
chalk.yellow(`File ${filepath} already exists. Overwrite? (y/N): `)
|
|
55
|
-
);
|
|
56
|
-
return answer.toLowerCase() === 'y';
|
|
57
|
-
}
|
|
58
|
-
|
|
59
54
|
async function validateAndProcess() {
|
|
60
55
|
try {
|
|
61
56
|
// Validate input file
|
|
@@ -75,33 +70,62 @@ async function validateAndProcess() {
|
|
|
75
70
|
process.exit(1);
|
|
76
71
|
}
|
|
77
72
|
|
|
78
|
-
//
|
|
73
|
+
// Handle Tailwind configuration
|
|
74
|
+
let tailwindConfig = null;
|
|
75
|
+
if (options.tw) {
|
|
76
|
+
if (options.twConfig) {
|
|
77
|
+
try {
|
|
78
|
+
tailwindConfig = await loadTailwindConfig(options.twConfig);
|
|
79
|
+
console.log(chalk.blue('Using custom Tailwind configuration'));
|
|
80
|
+
} catch (error) {
|
|
81
|
+
console.error(chalk.red(`Error loading Tailwind config: ${error.message}`));
|
|
82
|
+
process.exit(1);
|
|
83
|
+
}
|
|
84
|
+
} else {
|
|
85
|
+
console.log(chalk.blue('Using default Tailwind configuration'));
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Validate Tailwind config usage
|
|
90
|
+
if (options.twConfig && !options.tw) {
|
|
91
|
+
console.error(chalk.yellow('Warning: Tailwind config provided but Tailwind is not enabled. Use --tw to enable Tailwind.'));
|
|
92
|
+
process.exit(1);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Determine output directory
|
|
79
96
|
let outputDir = process.cwd();
|
|
80
97
|
if (options.output) {
|
|
81
98
|
outputDir = path.resolve(options.output);
|
|
82
|
-
// Create output directory if it doesn't exist
|
|
83
|
-
await fs.mkdir(outputDir, { recursive: true });
|
|
84
99
|
}
|
|
85
100
|
|
|
86
101
|
const outputPath = path.join(outputDir, 'output.html');
|
|
87
102
|
|
|
88
|
-
// Check if output file exists
|
|
89
|
-
if (await
|
|
103
|
+
// Check if output file exists
|
|
104
|
+
if (await fs.access(outputPath).then(() => true).catch(() => false)) {
|
|
90
105
|
if (!options.force) {
|
|
91
|
-
const shouldProceed = await
|
|
92
|
-
|
|
106
|
+
const shouldProceed = await question(
|
|
107
|
+
chalk.yellow(`File ${outputPath} already exists. Overwrite? (y/N): `)
|
|
108
|
+
);
|
|
109
|
+
if (shouldProceed.toLowerCase() !== 'y') {
|
|
93
110
|
console.log(chalk.yellow('Operation cancelled.'));
|
|
94
111
|
process.exit(0);
|
|
95
112
|
}
|
|
96
113
|
}
|
|
97
114
|
}
|
|
98
115
|
|
|
99
|
-
console.log(chalk.blue(
|
|
116
|
+
console.log(chalk.blue('Starting build process...'));
|
|
100
117
|
console.log(chalk.gray(`Input: ${inputPath}`));
|
|
101
|
-
console.log(chalk.gray(`Output: ${
|
|
118
|
+
console.log(chalk.gray(`Output directory: ${outputDir}`));
|
|
119
|
+
if (options.tw) {
|
|
120
|
+
console.log(chalk.gray('Tailwind CSS enabled'));
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const buildOptions = {
|
|
124
|
+
useTailwind: options.tw || false,
|
|
125
|
+
tailwindConfig: tailwindConfig
|
|
126
|
+
};
|
|
102
127
|
|
|
103
|
-
|
|
104
|
-
await buildStaticFile(inputPath, outputDir);
|
|
128
|
+
await buildStaticFile(inputPath, outputDir, buildOptions);
|
|
105
129
|
|
|
106
130
|
console.log(chalk.green('\n✨ Build completed successfully!'));
|
|
107
131
|
console.log(chalk.gray(`Output file: ${outputPath}`));
|
package/package.json
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "svelte-bundle",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.0.22",
|
|
4
|
+
"description": "CLI tool to easily bundle a .svelte file into a single .html file",
|
|
5
5
|
"type": "module",
|
|
6
|
+
"npm": "svelte-bundle",
|
|
6
7
|
"bin": {
|
|
7
8
|
"svelte-bundle": "./cli.js"
|
|
8
9
|
},
|
|
@@ -10,6 +11,10 @@
|
|
|
10
11
|
"cli.js",
|
|
11
12
|
"bundle.js"
|
|
12
13
|
],
|
|
14
|
+
"categories": [
|
|
15
|
+
"build-plugins",
|
|
16
|
+
"integrations"
|
|
17
|
+
],
|
|
13
18
|
"keywords": [
|
|
14
19
|
"svelte",
|
|
15
20
|
"bundle",
|
|
@@ -37,12 +42,16 @@
|
|
|
37
42
|
"@rollup/plugin-commonjs": "^24.0.1",
|
|
38
43
|
"@rollup/plugin-node-resolve": "^15.0.2",
|
|
39
44
|
"@rollup/plugin-terser": "^0.4.0",
|
|
45
|
+
"autoprefixer": "^10.4.20",
|
|
40
46
|
"chalk": "^5.3.0",
|
|
41
47
|
"commander": "^11.0.0",
|
|
48
|
+
"cssnano": "^7.0.6",
|
|
42
49
|
"rollup": "^3.20.0",
|
|
43
50
|
"rollup-plugin-css-only": "^4.3.0",
|
|
51
|
+
"rollup-plugin-postcss": "^4.0.2",
|
|
44
52
|
"rollup-plugin-svelte": "^7.1.4",
|
|
45
|
-
"svelte": "^3.58.0"
|
|
53
|
+
"svelte": "^3.58.0",
|
|
54
|
+
"tailwindcss": "^3.4.14"
|
|
46
55
|
},
|
|
47
56
|
"devDependencies": {
|
|
48
57
|
"@vitest/coverage-v8": "^2.1.3",
|