@anydigital/11ty-bricks 1.0.0-alpha → 1.0.0-alpha.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/README.md CHANGED
@@ -21,7 +21,7 @@ Import and use the entire plugin. You can configure which helpers to enable usin
21
21
  import eleventyBricks from "@anydigital/11ty-bricks";
22
22
 
23
23
  export default function(eleventyConfig) {
24
- eleventyBricks(eleventyConfig, {
24
+ eleventyConfig.addPlugin(eleventyBricks, {
25
25
  autoRaw: true // Enable autoRaw preprocessor (default: false)
26
26
  });
27
27
 
@@ -34,7 +34,7 @@ export default function(eleventyConfig) {
34
34
  const eleventyBricks = require("@anydigital/11ty-bricks");
35
35
 
36
36
  module.exports = function(eleventyConfig) {
37
- eleventyBricks(eleventyConfig, {
37
+ eleventyConfig.addPlugin(eleventyBricks, {
38
38
  autoRaw: true // Enable autoRaw preprocessor (default: false)
39
39
  });
40
40
 
@@ -78,12 +78,12 @@ When using the plugin (Option 1), you can configure which helpers to enable:
78
78
 
79
79
  **Example:**
80
80
  ```javascript
81
- eleventyBricks(eleventyConfig, {
81
+ eleventyConfig.addPlugin(eleventyBricks, {
82
82
  autoRaw: true
83
83
  });
84
84
  ```
85
85
 
86
- ## Available Helpers
86
+ ## Available 11ty Helpers
87
87
 
88
88
  ### autoRaw
89
89
 
@@ -102,6 +102,58 @@ Use {{ variable }} to output variables.
102
102
 
103
103
  Would try to process `{{ variable }}` as a template variable. With `autoRaw`, it displays exactly as written.
104
104
 
105
+ ## CLI Helper Commands
106
+
107
+ After installing this package, the `download-files` command becomes available:
108
+
109
+ ### download-files
110
+
111
+ A CLI command that downloads external files to your project based on URLs specified in your `package.json`.
112
+
113
+ **Usage:**
114
+
115
+ 1. Add a `_downloadFiles` field to your project's `package.json` with URL-to-path mappings:
116
+
117
+ ```json
118
+ {
119
+ "_downloadFiles": {
120
+ "https://example.com/library.js": "src/vendor/library.js",
121
+ "https://cdn.example.com/styles.css": "public/css/external.css"
122
+ }
123
+ }
124
+ ```
125
+
126
+ 2. Run the download command:
127
+
128
+ ```bash
129
+ npx download-files
130
+ ```
131
+
132
+ **Options:**
133
+
134
+ - `-o, --output <dir>`: Specify an output directory where all files will be downloaded (relative paths in `_downloadFiles` will be resolved relative to this directory)
135
+
136
+ ```bash
137
+ # Download all files to a specific directory
138
+ npx download-files --output public
139
+ ```
140
+
141
+ **Features:**
142
+
143
+ - Downloads multiple files from external URLs
144
+ - Automatically creates directories if they don't exist
145
+ - Overwrites existing files
146
+ - Continues downloading remaining files even if some fail
147
+ - Provides clear progress and error messages
148
+ - Returns appropriate exit codes for CI/CD integration
149
+
150
+ **Use Cases:**
151
+
152
+ - Download third-party libraries and assets
153
+ - Fetch external resources during build processes
154
+ - Keep vendored files up to date
155
+ - Automate dependency downloads that aren't available via npm
156
+
105
157
  ## Requirements
106
158
 
107
159
  - Node.js >= 18.0.0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@anydigital/11ty-bricks",
3
- "version": "1.0.0-alpha",
3
+ "version": "1.0.0-alpha.3",
4
4
  "description": "A collection of helpful utilities and filters for Eleventy (11ty)",
5
5
  "type": "module",
6
6
  "main": "./src/index.js",
@@ -10,11 +10,15 @@
10
10
  "require": "./src/index.cjs"
11
11
  }
12
12
  },
13
+ "bin": {
14
+ "download-files": "./src/cli/download-files.js"
15
+ },
13
16
  "files": [
14
17
  "src"
15
18
  ],
16
19
  "scripts": {
17
- "test": "node --test src/**/*.test.js"
20
+ "test": "node --test src/**/*.test.js",
21
+ "test2": "npx download-files && npx download-files --output ~subtest"
18
22
  },
19
23
  "keywords": [
20
24
  "11ty",
@@ -40,5 +44,9 @@
40
44
  },
41
45
  "engines": {
42
46
  "node": ">=18.0.0"
47
+ },
48
+ "_downloadFiles": {
49
+ "https://raw.githubusercontent.com/anydigital/11ty-bricks/refs/heads/main/README.md": "~README.md",
50
+ "https://raw.githubusercontent.com/anydigital/11ty-bricks/refs/heads/main/LICENSE": "~test/LICENSE"
43
51
  }
44
52
  }
@@ -0,0 +1,136 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { readFile, writeFile, mkdir } from 'fs/promises';
4
+ import { dirname, resolve, join } from 'path';
5
+
6
+ /**
7
+ * Parse command line arguments
8
+ *
9
+ * @returns {Object} Parsed arguments
10
+ */
11
+ function parseArgs() {
12
+ const args = process.argv.slice(2);
13
+ const parsed = {
14
+ outputDir: null
15
+ };
16
+
17
+ for (let i = 0; i < args.length; i++) {
18
+ const arg = args[i];
19
+
20
+ if (arg === '--output' || arg === '-o') {
21
+ if (i + 1 < args.length) {
22
+ parsed.outputDir = args[i + 1];
23
+ i++; // Skip next argument
24
+ } else {
25
+ throw new Error(`${arg} requires a directory path`);
26
+ }
27
+ }
28
+ }
29
+
30
+ return parsed;
31
+ }
32
+
33
+ /**
34
+ * Downloads files specified in package.json's _downloadFiles field
35
+ *
36
+ * @param {string|null} outputDir - Optional output directory to prepend to all paths
37
+ * @returns {Promise<boolean>} True if all downloads succeeded, false if any failed
38
+ */
39
+ async function download(outputDir = null) {
40
+ try {
41
+ // Find and read package.json from the current working directory
42
+ const packageJsonPath = resolve(process.cwd(), 'package.json');
43
+ const packageJson = JSON.parse(await readFile(packageJsonPath, 'utf-8'));
44
+
45
+ const downloadFiles = packageJson._downloadFiles;
46
+
47
+ if (!downloadFiles || typeof downloadFiles !== 'object') {
48
+ console.log('No _downloadFiles field found in package.json');
49
+ return true;
50
+ }
51
+
52
+ const entries = Object.entries(downloadFiles);
53
+
54
+ if (entries.length === 0) {
55
+ console.log('No files to download (_downloadFiles is empty)');
56
+ return true;
57
+ }
58
+
59
+ console.log(`Starting download of ${entries.length} file(s)...\n`);
60
+
61
+ let hasErrors = false;
62
+
63
+ // Process all downloads
64
+ for (const entry of entries) {
65
+ const url = entry[0];
66
+ let localPath = entry[1];
67
+
68
+ try {
69
+ console.log(`Downloading: ${url}`);
70
+ console.log(` To: ${localPath}`);
71
+
72
+ // Download the file
73
+ const response = await fetch(url);
74
+
75
+ if (!response.ok) {
76
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
77
+ }
78
+
79
+ // Get the file content
80
+ const content = await response.arrayBuffer();
81
+ const buffer = Buffer.from(content);
82
+
83
+ // Prepend output directory to local path if specified
84
+ if (outputDir) {
85
+ localPath = join(outputDir, localPath);
86
+ }
87
+
88
+ // Resolve the full path
89
+ const fullPath = resolve(process.cwd(), localPath);
90
+
91
+ // Create directory if it doesn't exist
92
+ const dir = dirname(fullPath);
93
+ await mkdir(dir, { recursive: true });
94
+
95
+ // Write the file
96
+ await writeFile(fullPath, buffer);
97
+
98
+ console.log(` Success: ${localPath}\n`);
99
+ } catch (error) {
100
+ hasErrors = true;
101
+ console.error(` Error: ${error.message}`);
102
+ console.error(` URL: ${url}\n`);
103
+ }
104
+ }
105
+
106
+ // Summary
107
+ if (hasErrors) {
108
+ console.log('Download completed with errors');
109
+ return false;
110
+ } else {
111
+ console.log('All downloads completed successfully');
112
+ return true;
113
+ }
114
+
115
+ } catch (error) {
116
+ console.error(`Fatal error: ${error.message}`);
117
+ return false;
118
+ }
119
+ }
120
+
121
+ /**
122
+ * CLI entry point
123
+ */
124
+ async function main() {
125
+ try {
126
+ const args = parseArgs();
127
+ const success = await download(args.outputDir);
128
+ process.exit(success ? 0 : 1);
129
+ } catch (error) {
130
+ console.error(`Error: ${error.message}`);
131
+ process.exit(1);
132
+ }
133
+ }
134
+
135
+ main();
136
+