saladplate 0.1.2 → 0.2.0

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
@@ -2,20 +2,22 @@
2
2
 
3
3
  _Simple templating; it's smaller than usual._
4
4
 
5
- Saladplate is a very simple templating tool, with 3 features:
5
+ Saladplate is a very simple templating tool, with 4 features:
6
6
 
7
7
  1. Replace `${{ VAR }}` with the contents of the environment variable `VAR`.
8
8
  2. Replace `$<< filename >>` with the contents of the file `filename`.
9
- 3. Replace `$(( some command ))` with the output of the command `some command`.
9
+ 3. Replace `$^^ filename ^^` _and everything after it_ with the contents of the file `filename`,
10
+ replacing `^^` in _that_ content with the same "everything after it".
11
+ 4. Replace `$(( some command ))` with the output of the command `some command`.
10
12
 
11
- If you need more functionality that that, this is not the templating tool for you.
13
+ If you need more functionality than that, this is not the templating tool for you.
12
14
 
13
15
  ## Installation
14
16
 
15
17
  ```bash
16
- $ npm -g install saladplate
18
+ $ npm install -g saladplate
17
19
  $ saladplate --version
18
- saladplate: version 0.1.0
20
+ saladplate: version 0.2.0
19
21
  ```
20
22
 
21
23
  ## Usage
@@ -57,27 +59,39 @@ You can also use the exported `template` function to template a string:
57
59
 
58
60
  ```typescript
59
61
  import { template } from "saladplate";
60
- console.info(template("The current PATH is: $PATH"));
62
+ console.info(await template("The current PATH is: ${{ PATH }}"));
61
63
  ```
62
64
 
63
65
  ## Development
64
66
 
65
- When developing locally, you can test your changes using the *script* `saladplate`,
66
- which uses `ts-node`. Note the use of `run` below, as well as the use of `--` to
67
- separate arguments to `npm run` from arguments to `saladplate`:
67
+ When developing locally, you can test your changes using the _script_ `saladplate`,
68
+ which uses `bun`. Note the use of `run` below, as well as the use of `--` to
69
+ separate arguments to `bun run` from arguments to `saladplate`:
68
70
 
69
71
  ```bash
70
- $ npm run saladplate -- --version
72
+ $ bun run saladplate -- --version
73
+ saladplate: version 0.2.0
74
+ ```
75
+
76
+ ### Building and publishing
77
+
78
+ To build the distributable JS:
79
+
80
+ ```bash
81
+ $ bun run build
82
+ ```
71
83
 
72
- > saladplate@0.1.0 saladplate
73
- > ts-node ./bin/saladplate.ts
84
+ This will build to `dist/`. To publish to NPM:
74
85
 
75
- saladplate: version 0.1.0
86
+ ```bash
87
+ $ npm publish
76
88
  ```
77
89
 
90
+ ### Testing
91
+
78
92
  Tests are located in `tests/` and consist of input templates `tests/*.test` and
79
93
  corresponding expected outputs `tests/*.expected`. To run tests:
80
94
 
81
95
  ```bash
82
- $ npm run test
96
+ $ bun run test
83
97
  ```
@@ -1,161 +1,229 @@
1
- #! /usr/bin/env node
2
- "use strict";
3
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
4
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
5
- return new (P || (P = Promise))(function (resolve, reject) {
6
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
7
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
8
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
9
- step((generator = generator.apply(thisArg, _arguments || [])).next());
10
- });
1
+ #!/usr/bin/env node
2
+ // @bun
3
+
4
+ // bin/saladplate.ts
5
+ import { open, readFile as readFile2, writeFile } from "fs/promises";
6
+ import path2 from "path";
7
+ import process2 from "process";
8
+ import { parseArgs } from "util";
9
+
10
+ // src/index.ts
11
+ import { exec as execCallback } from "node:child_process";
12
+ import { readFile } from "node:fs/promises";
13
+ import path from "node:path";
14
+ import process from "node:process";
15
+ import { promisify } from "node:util";
16
+ var exec = promisify(execCallback);
17
+ var BIN = "saladplate";
18
+ var cwdForFilename = (filename) => {
19
+ return filename === "/dev/stdin" ? process.cwd() : path.dirname(filename);
11
20
  };
12
- var __importDefault = (this && this.__importDefault) || function (mod) {
13
- return (mod && mod.__esModule) ? mod : { "default": mod };
21
+ var VAR_RE = /\$\{\{([^\}]+)\}\}/g;
22
+ var FILE_RE = /\$\<\<([^\>]+)\>\>/g;
23
+ var EXEC_RE = /\$\(\(([^\)]+)\)\)/g;
24
+ var INJECT_RE = /\$\^\^([^\^]+)\^\^/;
25
+ async function replaceAsync(input, regexp, fn) {
26
+ const replacements = await Promise.all(Array.from(input.matchAll(regexp), (match) => fn(match)));
27
+ let i = 0;
28
+ return input.replace(regexp, () => replacements[i++]);
29
+ }
30
+ var replaceVar = (variable, filename, options) => {
31
+ options.debug && console.debug(`${BIN}: ${filename}: evaluating ${variable} for replacement...`);
32
+ return process.env[variable] ?? "";
33
+ };
34
+ var replaceFile = async (includeFilename, filename, options) => {
35
+ const cwd = cwdForFilename(filename);
36
+ const include = path.join(cwd, includeFilename);
37
+ options.debug && console.debug(`${BIN}: ${filename}: reading ${include} for replacement...`);
38
+ const content = await readFile(include, { encoding: "utf-8" });
39
+ return template(content, include, options);
40
+ };
41
+ var replaceExec = async (command, filename, options) => {
42
+ const cwd = cwdForFilename(filename);
43
+ options.debug && console.debug(`${BIN}: ${filename}: executing ${command} for replacement...`);
44
+ const { stdout } = await exec(command, { cwd });
45
+ return stdout;
14
46
  };
15
- Object.defineProperty(exports, "__esModule", { value: true });
16
- const fs_1 = __importDefault(require("fs"));
17
- const path_1 = __importDefault(require("path"));
18
- const process_1 = __importDefault(require("process"));
19
- const util_1 = __importDefault(require("util"));
20
- const index_1 = require("../src/index");
21
- const parseArgsConfig = {
22
- // Exclude the first two arguments: node and the script itself.
23
- args: process_1.default.argv.slice(2),
24
- options: {
25
- debug: {
26
- type: "boolean",
27
- default: false,
28
- description: "enable debug mode",
29
- },
30
- version: {
31
- type: "boolean",
32
- default: false,
33
- short: "v",
34
- description: "show version information",
35
- },
36
- help: {
37
- type: "boolean",
38
- default: false,
39
- short: "h",
40
- description: "show this help message",
41
- },
42
- directory: {
43
- type: "string",
44
- short: "d",
45
- description: "output directory",
46
- },
47
- suffix: {
48
- type: "string",
49
- short: "s",
50
- description: "output suffix; only applies when using --directory",
51
- },
52
- output: {
53
- type: "string",
54
- short: "o",
55
- description: "output file; overrides --directory and --suffix",
56
- },
47
+ var processInjections = async (content, filename, options) => {
48
+ const match = INJECT_RE.exec(content);
49
+ if (!match) {
50
+ return content;
51
+ }
52
+ const injectFilename = match[1].trim();
53
+ let contentBefore = content.slice(0, match.index);
54
+ const rest = content.slice(match.index + match[0].length);
55
+ const innerResult = await processInjections(rest, filename, options);
56
+ const cwd = cwdForFilename(filename);
57
+ const inject = path.join(cwd, injectFilename);
58
+ options.debug && console.debug(`${BIN}: ${filename}: reading ${inject} for injection...`);
59
+ const wrapperContent = await readFile(inject, { encoding: "utf-8" });
60
+ let pattern = "^^";
61
+ if (innerResult.startsWith(`
62
+ `) && wrapperContent.includes(`
63
+ ^^`)) {
64
+ pattern = `
65
+ ^^`;
66
+ }
67
+ if (innerResult.endsWith(`
68
+ `) && wrapperContent.includes(`^^
69
+ `)) {
70
+ pattern = pattern + `
71
+ `;
72
+ }
73
+ const wrapped = wrapperContent.replace(pattern, innerResult);
74
+ if (contentBefore.endsWith(`
75
+ `) && innerResult.startsWith(`
76
+ `)) {
77
+ contentBefore = contentBefore.slice(0, -1);
78
+ }
79
+ return contentBefore + wrapped;
80
+ };
81
+ var template = async (content, filename, options) => {
82
+ filename ??= "/dev/stdin";
83
+ options ??= {};
84
+ content = await replaceAsync(content, VAR_RE, (match) => {
85
+ return replaceVar(match[1].trim(), filename, options);
86
+ });
87
+ content = await replaceAsync(content, FILE_RE, (match) => {
88
+ return replaceFile(match[1].trim(), filename, options);
89
+ });
90
+ content = await replaceAsync(content, EXEC_RE, (match) => {
91
+ return replaceExec(match[1].trim(), filename, options);
92
+ });
93
+ content = await processInjections(content, filename, options);
94
+ content = content.replace(/\n+$/m, `
95
+ `);
96
+ return content;
97
+ };
98
+
99
+ // bin/saladplate.ts
100
+ var parseArgsConfig = {
101
+ args: process2.argv.slice(2),
102
+ options: {
103
+ debug: {
104
+ type: "boolean",
105
+ default: false,
106
+ description: "enable debug mode"
107
+ },
108
+ version: {
109
+ type: "boolean",
110
+ default: false,
111
+ short: "v",
112
+ description: "show version information"
57
113
  },
58
- allowPositionals: true,
114
+ help: {
115
+ type: "boolean",
116
+ default: false,
117
+ short: "h",
118
+ description: "show this help message"
119
+ },
120
+ directory: {
121
+ type: "string",
122
+ short: "d",
123
+ description: "output directory"
124
+ },
125
+ suffix: {
126
+ type: "string",
127
+ short: "s",
128
+ description: "output suffix; only applies when using --directory"
129
+ },
130
+ output: {
131
+ type: "string",
132
+ short: "o",
133
+ description: "output file; overrides --directory and --suffix"
134
+ }
135
+ },
136
+ allowPositionals: true
59
137
  };
60
138
  function help() {
61
- console.info(`Usage: ${index_1.BIN} [options] <file>...`);
62
- console.info(`Options:`);
63
- const entries = Object.entries(parseArgsConfig.options);
64
- const prefixes = {};
65
- entries.forEach(([name, option]) => {
66
- const short = "short" in option ? `-${option.short}, ` : "";
67
- const prefix = ` ${short}--${name}`;
68
- prefixes[name] = prefix;
69
- });
70
- const len = Math.max(...entries.map(([name, option]) => prefixes[name].length)) + 2;
71
- entries.forEach(([name, option]) => {
72
- var _a;
73
- const prefix = prefixes[name];
74
- console.info(`${prefix}${" ".repeat(len - prefix.length)}${(_a = option.description) !== null && _a !== void 0 ? _a : ""}`);
75
- });
76
- console.info("");
139
+ console.info(`Usage: ${BIN} [options] <file>...`);
140
+ console.info(`Options:`);
141
+ const entries = Object.entries(parseArgsConfig.options);
142
+ const prefixes = {};
143
+ entries.forEach(([name, option]) => {
144
+ const short = "short" in option ? `-${option.short}, ` : "";
145
+ const prefix = ` ${short}--${name}`;
146
+ prefixes[name] = prefix;
147
+ });
148
+ const len = Math.max(...entries.map(([name, option]) => prefixes[name].length)) + 2;
149
+ entries.forEach(([name, option]) => {
150
+ const prefix = prefixes[name];
151
+ console.info(`${prefix}${" ".repeat(len - prefix.length)}${option.description ?? ""}`);
152
+ });
153
+ console.info("");
77
154
  }
155
+ var SALADPLATE_VERSION = "0.2.0";
78
156
  function version() {
79
- const version = process_1.default.env.npm_package_version;
80
- console.info(`${index_1.BIN}: version ${version !== null && version !== void 0 ? version : "unknown"}`);
157
+ let version2 = SALADPLATE_VERSION;
158
+ if (version2.startsWith("${{")) {
159
+ version2 = process2.env.npm_package_version ?? "";
160
+ }
161
+ console.info(`${BIN}: version ${version2 || "unknown"}`);
81
162
  }
82
- const parseArgs = () => {
83
- const { values: options, positionals: filenames } = util_1.default.parseArgs(parseArgsConfig);
84
- return { options, filenames };
85
- };
86
- let output = null;
87
- function outputForFilename(filename, options) {
88
- return __awaiter(this, void 0, void 0, function* () {
89
- if (output) {
90
- return output;
91
- }
92
- if (options.output) {
93
- output = {
94
- file: yield fs_1.default.promises.open(options.output, "w"),
95
- filename: options.output,
96
- };
97
- return output;
98
- }
99
- if (options.directory) {
100
- const basename = path_1.default.basename(filename);
101
- let outputFilename = path_1.default.join(options.directory, basename);
102
- if (options.suffix) {
103
- outputFilename = outputFilename.replace(/\.[^.]+$/, options.suffix);
104
- }
105
- return {
106
- file: outputFilename,
107
- filename: outputFilename,
108
- };
109
- }
110
- output = {
111
- file: yield fs_1.default.promises.open("/dev/stdout", "w"),
112
- filename: "/dev/stdout",
113
- };
114
- return output;
115
- });
163
+ var output = null;
164
+ async function outputForFilename(filename, options) {
165
+ if (output) {
166
+ return output;
167
+ }
168
+ if (options.output) {
169
+ output = {
170
+ file: await open(options.output, "w"),
171
+ filename: options.output
172
+ };
173
+ return output;
174
+ }
175
+ if (options.directory) {
176
+ const basename = path2.basename(filename);
177
+ let outputFilename = path2.join(options.directory, basename);
178
+ if (options.suffix) {
179
+ outputFilename = outputFilename.replace(/\.[^.]+$/, options.suffix);
180
+ }
181
+ return {
182
+ file: outputFilename,
183
+ filename: outputFilename
184
+ };
185
+ }
186
+ output = {
187
+ file: await open("/dev/stdout", "w"),
188
+ filename: "/dev/stdout"
189
+ };
190
+ return output;
191
+ }
192
+ async function main() {
193
+ const { values: options, positionals: filenames } = parseArgs(parseArgsConfig);
194
+ if (options.debug) {
195
+ console.debug(`${BIN}: debug mode enabled`);
196
+ }
197
+ if (options.help) {
198
+ help();
199
+ return;
200
+ }
201
+ if (options.version) {
202
+ version();
203
+ return;
204
+ }
205
+ if (filenames.length === 0) {
206
+ console.error(`${BIN}: no input files; use -- for stdin`);
207
+ help();
208
+ return -1;
209
+ }
210
+ await Promise.all(filenames.map(async (filename) => {
211
+ if (filename === "--") {
212
+ filename = "/dev/stdin";
213
+ }
214
+ options.debug && console.debug(`${BIN}: reading ${filename}...`);
215
+ const content = await readFile2(filename, { encoding: "utf-8" });
216
+ options.debug && console.debug(`${BIN}: templating ${filename}...`);
217
+ const output2 = await template(content, filename, options);
218
+ const { file: outputFile, filename: outputFilename } = await outputForFilename(filename, options);
219
+ options.debug && console.debug(`${BIN}: writing ${outputFilename}...`);
220
+ await writeFile(outputFile, output2, { encoding: "utf-8" });
221
+ }));
116
222
  }
117
- function main() {
118
- return __awaiter(this, void 0, void 0, function* () {
119
- const { options, filenames } = parseArgs();
120
- if (options.debug) {
121
- console.debug(`${index_1.BIN}: debug mode enabled`);
122
- }
123
- if (options.help) {
124
- help();
125
- return;
126
- }
127
- if (options.version) {
128
- version();
129
- return;
130
- }
131
- if (filenames.length === 0) {
132
- console.error(`${index_1.BIN}: no input files; use -- for stdin`);
133
- help();
134
- return -1;
135
- }
136
- yield Promise.all(filenames.map((filename) => __awaiter(this, void 0, void 0, function* () {
137
- if (filename === "--") {
138
- filename = "/dev/stdin";
139
- }
140
- options.debug && console.debug(`${index_1.BIN}: reading ${filename}...`);
141
- const content = yield fs_1.default.promises.readFile(filename, {
142
- encoding: "utf-8",
143
- });
144
- options.debug && console.debug(`${index_1.BIN}: templating ${filename}...`);
145
- const output = yield (0, index_1.template)(content, filename, options);
146
- const { file: outputFile, filename: outputFilename } = yield outputForFilename(filename, options);
147
- options.debug && console.debug(`${index_1.BIN}: writing ${outputFilename}...`);
148
- yield fs_1.default.promises.writeFile(outputFile, output, {
149
- encoding: "utf-8",
150
- });
151
- })));
152
- });
223
+ try {
224
+ const exitCode = await main();
225
+ process2.exit(exitCode ?? 0);
226
+ } catch (e) {
227
+ console.error(`${BIN}: unhandled error:`, e);
228
+ process2.exit(-1);
153
229
  }
154
- main()
155
- .then((i) => {
156
- process_1.default.exit(i !== null && i !== void 0 ? i : 0);
157
- })
158
- .catch((e) => {
159
- console.error(`${index_1.BIN}: unhandled error:`, e);
160
- process_1.default.exit(-1);
161
- });
package/dist/src/index.js CHANGED
@@ -1,82 +1,92 @@
1
- "use strict";
2
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
- return new (P || (P = Promise))(function (resolve, reject) {
5
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
- step((generator = generator.apply(thisArg, _arguments || [])).next());
9
- });
1
+ // src/index.ts
2
+ import { exec as execCallback } from "node:child_process";
3
+ import { readFile } from "node:fs/promises";
4
+ import path from "node:path";
5
+ import process from "node:process";
6
+ import { promisify } from "node:util";
7
+ var exec = promisify(execCallback);
8
+ var BIN = "saladplate";
9
+ var cwdForFilename = (filename) => {
10
+ return filename === "/dev/stdin" ? process.cwd() : path.dirname(filename);
10
11
  };
11
- var __importDefault = (this && this.__importDefault) || function (mod) {
12
- return (mod && mod.__esModule) ? mod : { "default": mod };
12
+ var VAR_RE = /\$\{\{([^\}]+)\}\}/g;
13
+ var FILE_RE = /\$\<\<([^\>]+)\>\>/g;
14
+ var EXEC_RE = /\$\(\(([^\)]+)\)\)/g;
15
+ var INJECT_RE = /\$\^\^([^\^]+)\^\^/;
16
+ async function replaceAsync(input, regexp, fn) {
17
+ const replacements = await Promise.all(Array.from(input.matchAll(regexp), (match) => fn(match)));
18
+ let i = 0;
19
+ return input.replace(regexp, () => replacements[i++]);
20
+ }
21
+ var replaceVar = (variable, filename, options) => {
22
+ options.debug && console.debug(`${BIN}: ${filename}: evaluating ${variable} for replacement...`);
23
+ return process.env[variable] ?? "";
13
24
  };
14
- Object.defineProperty(exports, "__esModule", { value: true });
15
- exports.template = exports.BIN = void 0;
16
- const child_process_1 = require("child_process");
17
- const fs_1 = __importDefault(require("fs"));
18
- const path_1 = __importDefault(require("path"));
19
- const process_1 = __importDefault(require("process"));
20
- const util_1 = __importDefault(require("util"));
21
- /**
22
- * The name of the tool; prefixes most output.
23
- */
24
- exports.BIN = "saladplate";
25
- const cwdForFilename = (filename) => {
26
- return filename === "/dev/stdin" ? process_1.default.cwd() : path_1.default.dirname(filename);
25
+ var replaceFile = async (includeFilename, filename, options) => {
26
+ const cwd = cwdForFilename(filename);
27
+ const include = path.join(cwd, includeFilename);
28
+ options.debug && console.debug(`${BIN}: ${filename}: reading ${include} for replacement...`);
29
+ const content = await readFile(include, { encoding: "utf-8" });
30
+ return template(content, include, options);
27
31
  };
28
- const VAR_RE = /\$\{\{([^\}]+)\}\}/g;
29
- const FILE_RE = /\$\<\<([^\>]+)\>\>/g;
30
- const EXEC_RE = /\$\(\(([^\)]+)\)\)/g;
31
- function replaceAsync(input, regexp, fn) {
32
- return __awaiter(this, void 0, void 0, function* () {
33
- const replacements = yield Promise.all(Array.from(input.matchAll(regexp), (match) => fn(match)));
34
- let i = 0;
35
- return input.replace(regexp, () => replacements[i++]);
36
- });
37
- }
38
- const replaceVar = (variable, filename, options) => {
39
- var _a;
40
- options.debug && console.debug(`${exports.BIN}: ${filename}: evaluating ${variable} for replacement...`);
41
- return (_a = process_1.default.env[variable]) !== null && _a !== void 0 ? _a : "";
32
+ var replaceExec = async (command, filename, options) => {
33
+ const cwd = cwdForFilename(filename);
34
+ options.debug && console.debug(`${BIN}: ${filename}: executing ${command} for replacement...`);
35
+ const { stdout } = await exec(command, { cwd });
36
+ return stdout;
42
37
  };
43
- const replaceFile = (includeFilename, filename, options) => __awaiter(void 0, void 0, void 0, function* () {
44
- // Resolve the include filename relative to the current file; if the current file is stdin
45
- // then keep the current working directory.
46
- const cwd = cwdForFilename(filename);
47
- const include = path_1.default.join(cwd, includeFilename);
48
- options.debug && console.debug(`${exports.BIN}: ${filename}: reading ${include} for replacement...`);
49
- const content = yield fs_1.default.promises.readFile(include, { encoding: "utf-8" });
50
- return (0, exports.template)(content, include, options);
51
- });
52
- const replaceExec = (command, filename, options) => __awaiter(void 0, void 0, void 0, function* () {
53
- // Execute the command in the working directory containing the file we are processing; if
54
- // the current file is stdin then keep the current working directory.
55
- const cwd = cwdForFilename(filename);
56
- options.debug && console.debug(`${exports.BIN}: ${filename}: executing ${command} for replacement...`);
57
- const { stdout } = yield util_1.default.promisify(child_process_1.exec)(command, {
58
- cwd,
59
- });
60
- return stdout;
61
- });
62
- /**
63
- * Replace variables, file includes, and command executions in the given `content`, which
64
- * is assumed to have been read from the given `filename` (or stdin). Collapses newlines
65
- * at the end of the output so that there's just one.
66
- */
67
- const template = (content, filename, options) => __awaiter(void 0, void 0, void 0, function* () {
68
- filename !== null && filename !== void 0 ? filename : (filename = "/dev/stdin");
69
- options !== null && options !== void 0 ? options : (options = {});
70
- content = yield replaceAsync(content, VAR_RE, (match) => {
71
- return replaceVar(match[1].trim(), filename, options);
72
- });
73
- content = yield replaceAsync(content, FILE_RE, (match) => {
74
- return replaceFile(match[1].trim(), filename, options);
75
- });
76
- content = yield replaceAsync(content, EXEC_RE, (match) => {
77
- return replaceExec(match[1].trim(), filename, options);
78
- });
79
- content = content.replace(/\n+$/m, "\n");
38
+ var processInjections = async (content, filename, options) => {
39
+ const match = INJECT_RE.exec(content);
40
+ if (!match) {
80
41
  return content;
81
- });
82
- exports.template = template;
42
+ }
43
+ const injectFilename = match[1].trim();
44
+ let contentBefore = content.slice(0, match.index);
45
+ const rest = content.slice(match.index + match[0].length);
46
+ const innerResult = await processInjections(rest, filename, options);
47
+ const cwd = cwdForFilename(filename);
48
+ const inject = path.join(cwd, injectFilename);
49
+ options.debug && console.debug(`${BIN}: ${filename}: reading ${inject} for injection...`);
50
+ const wrapperContent = await readFile(inject, { encoding: "utf-8" });
51
+ let pattern = "^^";
52
+ if (innerResult.startsWith(`
53
+ `) && wrapperContent.includes(`
54
+ ^^`)) {
55
+ pattern = `
56
+ ^^`;
57
+ }
58
+ if (innerResult.endsWith(`
59
+ `) && wrapperContent.includes(`^^
60
+ `)) {
61
+ pattern = pattern + `
62
+ `;
63
+ }
64
+ const wrapped = wrapperContent.replace(pattern, innerResult);
65
+ if (contentBefore.endsWith(`
66
+ `) && innerResult.startsWith(`
67
+ `)) {
68
+ contentBefore = contentBefore.slice(0, -1);
69
+ }
70
+ return contentBefore + wrapped;
71
+ };
72
+ var template = async (content, filename, options) => {
73
+ filename ??= "/dev/stdin";
74
+ options ??= {};
75
+ content = await replaceAsync(content, VAR_RE, (match) => {
76
+ return replaceVar(match[1].trim(), filename, options);
77
+ });
78
+ content = await replaceAsync(content, FILE_RE, (match) => {
79
+ return replaceFile(match[1].trim(), filename, options);
80
+ });
81
+ content = await replaceAsync(content, EXEC_RE, (match) => {
82
+ return replaceExec(match[1].trim(), filename, options);
83
+ });
84
+ content = await processInjections(content, filename, options);
85
+ content = content.replace(/\n+$/m, `
86
+ `);
87
+ return content;
88
+ };
89
+ export {
90
+ template,
91
+ BIN
92
+ };
package/package.json CHANGED
@@ -1,24 +1,27 @@
1
1
  {
2
2
  "name": "saladplate",
3
- "version": "0.1.2",
3
+ "version": "0.2.0",
4
+ "type": "module",
4
5
  "description": "Very simple templating.",
5
6
  "keywords": [
6
7
  "template",
7
8
  "templating"
8
9
  ],
9
- "main": "./dist/src/saladplate.js",
10
+ "main": "./dist/src/index.js",
11
+ "exports": {
12
+ ".": "./dist/src/index.js"
13
+ },
10
14
  "bin": {
11
15
  "saladplate": "dist/bin/saladplate.js"
12
16
  },
13
17
  "scripts": {
14
18
  "types": "tsc --noEmit",
15
- "build": "./build.sh",
16
- "saladplate": "ts-node ./bin/saladplate.ts",
19
+ "build": "bun run types && ./build.sh",
20
+ "saladplate": "bun ./bin/saladplate.ts",
17
21
  "test": "./test.sh"
18
22
  },
19
23
  "devDependencies": {
20
24
  "@types/node": "^20.12.12",
21
- "ts-node": "^10.9.2",
22
25
  "typescript": "^5.4.5"
23
26
  },
24
27
  "files": [
@@ -1,2 +0,0 @@
1
- #! /usr/bin/env ts-node
2
- export {};
@@ -1,16 +0,0 @@
1
- /**
2
- * The name of the tool; prefixes most output.
3
- */
4
- export declare const BIN = "saladplate";
5
- /**
6
- * Templating options for `template`.
7
- */
8
- export type Options = {
9
- debug?: boolean;
10
- };
11
- /**
12
- * Replace variables, file includes, and command executions in the given `content`, which
13
- * is assumed to have been read from the given `filename` (or stdin). Collapses newlines
14
- * at the end of the output so that there's just one.
15
- */
16
- export declare const template: (content: string, filename?: string, options?: Options) => Promise<string>;