svelte-bundle 0.0.12 → 0.0.21

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 (4) hide show
  1. package/ReadMe.md +14 -17
  2. package/bundle.js +71 -26
  3. package/cli.js +49 -25
  4. package/package.json +7 -3
package/ReadMe.md CHANGED
@@ -1,3 +1,9 @@
1
+ ## Usage
2
+ This tool can be used with `npx`:
3
+ ```bash
4
+ npx svelte-bundle -i <input-dir> -o <output-dir>
5
+ ```
6
+
1
7
  # Svelte Bundle CLI
2
8
 
3
9
  **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.
@@ -19,20 +25,11 @@ So, I searched for tools around that could be of assistance. I found [figsvelte]
19
25
 
20
26
  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
27
 
22
- ## Features (In Progress)
23
- - [ ] Bundles Svelte applications using Vite and SSR.
24
- - [ ] Outputs a single `.html` file ready for deployment.
25
- - [ ] CLI arguments for specifying input and output directories.
26
-
27
- ## Roadmap
28
- - [ ] Add options for SSR hydration
29
- - [ ] Handle CSS and assets within the bundled file.
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>
28
+ ## Features
29
+ - [x] Bundles Svelte applications
30
+ - [x] Outputs a single `.html` file ready for deployment.
31
+ - [x] CLI arguments for specifying input and output directories.
32
+ - [x] Tests and CI integration.
33
+ - [x] Handle CSS and assets within the bundled file.
34
+ - [x] Add tailwindcss support, **this is in beta**.
35
+ - [x] Implement error handling and more robust validation.
package/bundle.js CHANGED
@@ -7,11 +7,62 @@ import resolve from '@rollup/plugin-node-resolve';
7
7
  import commonjs from '@rollup/plugin-commonjs';
8
8
  import css from 'rollup-plugin-css-only';
9
9
  import terser from '@rollup/plugin-terser';
10
+ import postcss from 'postcss';
11
+ import tailwindcss from 'tailwindcss';
12
+ import autoprefixer from 'autoprefixer';
13
+ import cssnano from 'cssnano';
14
+
15
+ export async function buildStaticFile(svelteFilePath, outputDir, options = {}) {
16
+ const { useTailwind = false, tailwindConfig = null } = options;
10
17
 
11
- export async function buildStaticFile(svelteFilePath, outputDir) {
12
18
  try {
19
+ // Ensure output directory exists
20
+ await fs.mkdir(outputDir, { recursive: true });
21
+
13
22
  let cssText = '';
14
23
 
24
+ // Setup PostCSS plugins based on whether Tailwind is enabled
25
+ const postcssPlugins = useTailwind
26
+ ? [
27
+ tailwindcss(tailwindConfig || {
28
+ content: [svelteFilePath],
29
+ theme: { extend: {} },
30
+ plugins: [],
31
+ }),
32
+ autoprefixer(),
33
+ cssnano({
34
+ preset: ['default', {
35
+ discardComments: {
36
+ removeAll: true,
37
+ },
38
+ }],
39
+ })
40
+ ]
41
+ : [
42
+ autoprefixer(),
43
+ cssnano({
44
+ preset: ['default', {
45
+ discardComments: {
46
+ removeAll: true,
47
+ },
48
+ }],
49
+ })
50
+ ];
51
+
52
+ // Process global styles
53
+ let globalCssText = '';
54
+ if (useTailwind) {
55
+ const tailwindCss = `
56
+ @tailwind base;
57
+ @tailwind components;
58
+ @tailwind utilities;
59
+ `;
60
+
61
+ const processedCss = await postcss(postcssPlugins)
62
+ .process(tailwindCss, { from: undefined });
63
+ globalCssText = processedCss.css;
64
+ }
65
+
15
66
  // Create temporary SSR bundle
16
67
  const ssrBundle = await rollup({
17
68
  input: svelteFilePath,
@@ -22,7 +73,15 @@ export async function buildStaticFile(svelteFilePath, outputDir) {
22
73
  hydratable: true,
23
74
  css: false
24
75
  },
25
- emitCss: true
76
+ emitCss: true,
77
+ preprocess: useTailwind ? {
78
+ style: async ({ content }) => {
79
+ if (!content) return { code: '' };
80
+ const result = await postcss(postcssPlugins)
81
+ .process(content, { from: undefined });
82
+ return { code: result.css };
83
+ }
84
+ } : undefined
26
85
  }),
27
86
  css({
28
87
  output: function(styles) {
@@ -66,7 +125,15 @@ export async function buildStaticFile(svelteFilePath, outputDir) {
66
125
  hydratable: true,
67
126
  css: false
68
127
  },
69
- emitCss: true
128
+ emitCss: true,
129
+ preprocess: useTailwind ? {
130
+ style: async ({ content }) => {
131
+ if (!content) return { code: '' };
132
+ const result = await postcss(postcssPlugins)
133
+ .process(content, { from: undefined });
134
+ return { code: result.css };
135
+ }
136
+ } : undefined
70
137
  }),
71
138
  css({
72
139
  output: function(styles) {
@@ -91,32 +158,10 @@ export async function buildStaticFile(svelteFilePath, outputDir) {
91
158
  });
92
159
 
93
160
  // 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>`;
161
+ 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
162
 
117
163
  // Write the output file
118
164
  const outputPath = path.join(outputDir, 'output.html');
119
- await fs.mkdir(outputDir, { recursive: true });
120
165
  await fs.writeFile(outputPath, finalHtml, 'utf-8');
121
166
  } catch (error) {
122
167
  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 checkFileExists(filepath) {
43
+ async function loadTailwindConfig(configPath) {
44
+ const fullPath = path.resolve(configPath);
44
45
  try {
45
- await fs.access(filepath);
46
- return true;
47
- } catch {
48
- return false;
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
- // Determine output directory and file path
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 and handle overwriting
89
- if (await checkFileExists(outputPath)) {
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 shouldOverwrite(outputPath);
92
- if (!shouldProceed) {
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(`Starting ${packageJson.name} v${packageJson.version} build process...`));
116
+ console.log(chalk.blue('Starting build process...'));
100
117
  console.log(chalk.gray(`Input: ${inputPath}`));
101
- console.log(chalk.gray(`Output: ${outputPath}`));
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
- // Process the file
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,6 +1,6 @@
1
1
  {
2
2
  "name": "svelte-bundle",
3
- "version": "0.0.12",
3
+ "version": "0.0.21",
4
4
  "description": "Bundle Svelte files into static HTML files",
5
5
  "type": "module",
6
6
  "bin": {
@@ -37,12 +37,16 @@
37
37
  "@rollup/plugin-commonjs": "^24.0.1",
38
38
  "@rollup/plugin-node-resolve": "^15.0.2",
39
39
  "@rollup/plugin-terser": "^0.4.0",
40
+ "autoprefixer": "^10.4.20",
40
41
  "chalk": "^5.3.0",
41
42
  "commander": "^11.0.0",
43
+ "cssnano": "^7.0.6",
42
44
  "rollup": "^3.20.0",
43
45
  "rollup-plugin-css-only": "^4.3.0",
46
+ "rollup-plugin-postcss": "^4.0.2",
44
47
  "rollup-plugin-svelte": "^7.1.4",
45
- "svelte": "^3.58.0"
48
+ "svelte": "^3.58.0",
49
+ "tailwindcss": "^3.4.14"
46
50
  },
47
51
  "devDependencies": {
48
52
  "@vitest/coverage-v8": "^2.1.3",
@@ -55,4 +59,4 @@
55
59
  "engines": {
56
60
  "node": ">=18"
57
61
  }
58
- }
62
+ }