sommark 3.3.3 → 3.3.4
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/cli/cli.mjs +3 -1
- package/cli/commands/init.js +9 -21
- package/cli/commands/print.js +8 -2
- package/cli/helpers/transpile.js +5 -1
- package/core/helpers/config-loader.js +31 -5
- package/core/transpiler.js +14 -11
- package/mappers/languages/mdx.js +9 -3
- package/package.json +1 -1
package/cli/cli.mjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { enableColor } from "../helpers/colorize.js";
|
|
3
|
+
|
|
3
4
|
import { getHelp } from "./commands/help.js";
|
|
4
5
|
import { printVersion, printHeader } from "./commands/version.js";
|
|
5
6
|
import { runBuild } from "./commands/build.js";
|
|
@@ -48,10 +49,11 @@ async function main() {
|
|
|
48
49
|
|
|
49
50
|
// 4. Init
|
|
50
51
|
if (command === "init") {
|
|
51
|
-
await runInit();
|
|
52
|
+
await runInit(args[1] === "--local" || args[1] === "-l");
|
|
52
53
|
return;
|
|
53
54
|
}
|
|
54
55
|
|
|
56
|
+
|
|
55
57
|
// 5. Show
|
|
56
58
|
if (command === "show") {
|
|
57
59
|
await runShow(args[1]);
|
package/cli/commands/init.js
CHANGED
|
@@ -18,42 +18,29 @@ export function getConfigDir() {
|
|
|
18
18
|
}
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
export async function runInit() {
|
|
21
|
+
export async function runInit(isLocal = false) {
|
|
22
22
|
try {
|
|
23
|
-
const configDir = getConfigDir();
|
|
23
|
+
const configDir = isLocal ? process.cwd() : getConfigDir();
|
|
24
24
|
const configFilePath = path.join(configDir, "smark.config.js");
|
|
25
25
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
await fs.mkdir(configDir, { recursive: true });
|
|
31
|
-
|
|
32
|
-
// ======================================================
|
|
33
|
-
// Default configuration content
|
|
34
|
-
// ======================================================
|
|
26
|
+
if (!isLocal) {
|
|
27
|
+
await fs.mkdir(configDir, { recursive: true });
|
|
28
|
+
}
|
|
35
29
|
|
|
36
30
|
const defaultConfigContent = `export default {
|
|
37
|
-
outputDir: "",
|
|
31
|
+
outputDir: "./",
|
|
38
32
|
outputFile: "output",
|
|
39
|
-
mappingFile:
|
|
33
|
+
mappingFile: null,
|
|
40
34
|
plugins: [],
|
|
41
35
|
priority: [],
|
|
36
|
+
excludePlugins: [],
|
|
42
37
|
};
|
|
43
38
|
`;
|
|
44
39
|
|
|
45
|
-
// ======================================================
|
|
46
|
-
// Check if config file already exists
|
|
47
|
-
// ======================================================
|
|
48
|
-
|
|
49
40
|
try {
|
|
50
41
|
await fs.access(configFilePath);
|
|
51
42
|
console.log(formatMessage(`<$yellow:Configuration already exists at:$> <$cyan:${configFilePath}$>`));
|
|
52
43
|
} catch {
|
|
53
|
-
// ======================================================
|
|
54
|
-
// Create default config file if it doesn't exist
|
|
55
|
-
// ======================================================
|
|
56
|
-
|
|
57
44
|
await fs.writeFile(configFilePath, defaultConfigContent, "utf-8");
|
|
58
45
|
console.log(formatMessage(`<$green:Initialized SomMark configuration at:$> <$cyan:${configFilePath}$>`));
|
|
59
46
|
}
|
|
@@ -63,3 +50,4 @@ export async function runInit() {
|
|
|
63
50
|
]);
|
|
64
51
|
}
|
|
65
52
|
}
|
|
53
|
+
|
package/cli/commands/print.js
CHANGED
|
@@ -41,9 +41,12 @@ export async function printLex(filePath) {
|
|
|
41
41
|
format: "text",
|
|
42
42
|
filename: absolutePath,
|
|
43
43
|
plugins: config.plugins,
|
|
44
|
-
priority: config.priority
|
|
44
|
+
priority: config.priority,
|
|
45
|
+
excludePlugins: config.excludePlugins,
|
|
46
|
+
includeDocument: config.includeDocument ?? true
|
|
45
47
|
});
|
|
46
48
|
|
|
49
|
+
|
|
47
50
|
const tokens = await smark.lex();
|
|
48
51
|
console.log(JSON.stringify(tokens, null, 2));
|
|
49
52
|
} else {
|
|
@@ -67,9 +70,12 @@ export async function printParse(filePath) {
|
|
|
67
70
|
format: "text",
|
|
68
71
|
filename: absolutePath,
|
|
69
72
|
plugins: config.plugins,
|
|
70
|
-
priority: config.priority
|
|
73
|
+
priority: config.priority,
|
|
74
|
+
excludePlugins: config.excludePlugins,
|
|
75
|
+
includeDocument: config.includeDocument ?? true
|
|
71
76
|
});
|
|
72
77
|
|
|
78
|
+
|
|
73
79
|
const ast = await smark.parse();
|
|
74
80
|
console.log(JSON.stringify(ast, null, 2));
|
|
75
81
|
} else {
|
package/cli/helpers/transpile.js
CHANGED
|
@@ -19,6 +19,8 @@ export async function transpile({ src, format, filename = null, mappingFile = ""
|
|
|
19
19
|
const config = await loadConfig(filename);
|
|
20
20
|
let finalMapper = mappingFile;
|
|
21
21
|
|
|
22
|
+
|
|
23
|
+
|
|
22
24
|
// 1. Resolve Mapping File
|
|
23
25
|
if (typeof mappingFile !== "object" || mappingFile === null) {
|
|
24
26
|
if (config.mappingFile) {
|
|
@@ -42,7 +44,9 @@ export async function transpile({ src, format, filename = null, mappingFile = ""
|
|
|
42
44
|
filename,
|
|
43
45
|
mapperFile: finalMapper,
|
|
44
46
|
plugins: config.plugins,
|
|
45
|
-
priority: config.priority
|
|
47
|
+
priority: config.priority,
|
|
48
|
+
excludePlugins: config.excludePlugins,
|
|
49
|
+
includeDocument: config.includeDocument ?? true
|
|
46
50
|
});
|
|
47
51
|
|
|
48
52
|
return await smark.transpile();
|
|
@@ -23,13 +23,16 @@ export function getConfigDir() {
|
|
|
23
23
|
* Recursively searches for smark.config.js up the directory tree starting from startDir.
|
|
24
24
|
*/
|
|
25
25
|
async function findConfig(startDir) {
|
|
26
|
-
let currentDir = startDir;
|
|
27
|
-
|
|
26
|
+
let currentDir = path.resolve(startDir);
|
|
27
|
+
const root = path.parse(currentDir).root;
|
|
28
|
+
|
|
29
|
+
while (true) {
|
|
28
30
|
const configPath = path.join(currentDir, CONFIG_FILE_NAME);
|
|
29
31
|
try {
|
|
30
32
|
await fs.access(configPath);
|
|
31
33
|
return configPath;
|
|
32
34
|
} catch {
|
|
35
|
+
if (currentDir === root) break;
|
|
33
36
|
currentDir = path.dirname(currentDir);
|
|
34
37
|
}
|
|
35
38
|
}
|
|
@@ -56,7 +59,17 @@ async function loadConfigFile(configPath) {
|
|
|
56
59
|
* Checks local directory, parent directories, and finally the global config directory.
|
|
57
60
|
*/
|
|
58
61
|
export async function findAndLoadConfig(targetPath) {
|
|
59
|
-
|
|
62
|
+
let startDir;
|
|
63
|
+
if (targetPath) {
|
|
64
|
+
try {
|
|
65
|
+
const stats = await fs.stat(targetPath);
|
|
66
|
+
startDir = stats.isDirectory() ? targetPath : path.dirname(targetPath);
|
|
67
|
+
} catch {
|
|
68
|
+
startDir = process.cwd();
|
|
69
|
+
}
|
|
70
|
+
} else {
|
|
71
|
+
startDir = process.cwd();
|
|
72
|
+
}
|
|
60
73
|
|
|
61
74
|
let configPath = await findConfig(startDir);
|
|
62
75
|
|
|
@@ -87,15 +100,28 @@ export async function findAndLoadConfig(targetPath) {
|
|
|
87
100
|
outputDir: startDir,
|
|
88
101
|
mappingFile: null,
|
|
89
102
|
plugins: [],
|
|
90
|
-
priority: []
|
|
103
|
+
priority: [],
|
|
104
|
+
excludePlugins: [],
|
|
105
|
+
includeDocument: true
|
|
91
106
|
};
|
|
92
107
|
|
|
93
108
|
if (configPath) {
|
|
94
109
|
const loadedConfig = await loadConfigFile(configPath);
|
|
95
110
|
if (loadedConfig) {
|
|
96
|
-
|
|
111
|
+
const finalConfig = { ...defaultConfig, ...loadedConfig, resolvedConfigPath: configPath };
|
|
112
|
+
|
|
113
|
+
// Ensure outputDir is resolved if it's relative in the config
|
|
114
|
+
if (loadedConfig.outputDir) {
|
|
115
|
+
const configDir = path.dirname(configPath);
|
|
116
|
+
finalConfig.outputDir = path.resolve(configDir, loadedConfig.outputDir);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return finalConfig;
|
|
97
120
|
}
|
|
98
121
|
}
|
|
99
122
|
|
|
100
123
|
return { ...defaultConfig, resolvedConfigPath: null };
|
|
101
124
|
}
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
|
package/core/transpiler.js
CHANGED
|
@@ -71,7 +71,7 @@ async function generateOutput(ast, i, format, mapper_file) {
|
|
|
71
71
|
switch (body_node.type) {
|
|
72
72
|
case TEXT:
|
|
73
73
|
const shouldEscapeText = target.options?.escape !== false;
|
|
74
|
-
context += (format === htmlFormat
|
|
74
|
+
context += (format === htmlFormat) && shouldEscapeText ? escapeHTML(body_node.text) : body_node.text;
|
|
75
75
|
break;
|
|
76
76
|
|
|
77
77
|
case INLINE:
|
|
@@ -85,7 +85,7 @@ async function generateOutput(ast, i, format, mapper_file) {
|
|
|
85
85
|
context +=
|
|
86
86
|
inlineTarget.render.call(mapper_file, {
|
|
87
87
|
args: body_node.args.length > 0 ? body_node.args : [],
|
|
88
|
-
content: (format === htmlFormat
|
|
88
|
+
content: (format === htmlFormat) && shouldEscapeInline ? escapeHTML(body_node.value) : body_node.value,
|
|
89
89
|
ast: body_node
|
|
90
90
|
}) + (format === mdxFormat ? "\n" : "");
|
|
91
91
|
}
|
|
@@ -99,11 +99,12 @@ async function generateOutput(ast, i, format, mapper_file) {
|
|
|
99
99
|
if (atTarget) {
|
|
100
100
|
const shouldEscapeAt = atTarget.options?.escape !== false;
|
|
101
101
|
let content = body_node.content;
|
|
102
|
-
if (shouldEscapeAt && (format === htmlFormat
|
|
102
|
+
if (shouldEscapeAt && (format === htmlFormat)) {
|
|
103
103
|
content = escapeHTML(content);
|
|
104
104
|
}
|
|
105
|
-
const rendered = atTarget.render.call(mapper_file, { args: body_node.args, content, ast: body_node })
|
|
106
|
-
|
|
105
|
+
const rendered = atTarget.render.call(mapper_file, { args: body_node.args, content, ast: body_node });
|
|
106
|
+
const finalAtRendered = (format === mdxFormat) ? rendered : rendered.trimEnd() + "\n";
|
|
107
|
+
context = context.trim() ? context.trimEnd() + "\n" + finalAtRendered : context + finalAtRendered;
|
|
107
108
|
}
|
|
108
109
|
break;
|
|
109
110
|
|
|
@@ -145,7 +146,8 @@ async function generateOutput(ast, i, format, mapper_file) {
|
|
|
145
146
|
]);
|
|
146
147
|
}
|
|
147
148
|
const newline = (format === mdxFormat && node.body.length <= 1) ? "" : "\n";
|
|
148
|
-
|
|
149
|
+
const finalResult = (format === mdxFormat) ? result : result.trimEnd();
|
|
150
|
+
return finalResult + newline;
|
|
149
151
|
}
|
|
150
152
|
|
|
151
153
|
// ========================================================================== //
|
|
@@ -164,7 +166,7 @@ async function transpiler({ ast, format, mapperFile, includeDocument = true }) {
|
|
|
164
166
|
output += mapperFile.comment(node.text);
|
|
165
167
|
break;
|
|
166
168
|
case TEXT:
|
|
167
|
-
const shouldEscapeText = (format === htmlFormat
|
|
169
|
+
const shouldEscapeText = (format === htmlFormat);
|
|
168
170
|
output += shouldEscapeText ? escapeHTML(node.text) : node.text;
|
|
169
171
|
break;
|
|
170
172
|
case INLINE:
|
|
@@ -174,7 +176,7 @@ async function transpiler({ ast, format, mapperFile, includeDocument = true }) {
|
|
|
174
176
|
const shouldEscapeInline = inlineTarget.options?.escape !== false;
|
|
175
177
|
output += inlineTarget.render.call(mapperFile, {
|
|
176
178
|
args: node.args.length > 0 ? node.args : [],
|
|
177
|
-
content: (format === htmlFormat
|
|
179
|
+
content: (format === htmlFormat) && shouldEscapeInline ? escapeHTML(node.value) : node.value,
|
|
178
180
|
ast: node
|
|
179
181
|
}) + (format === mdxFormat ? "\n" : "");
|
|
180
182
|
}
|
|
@@ -185,11 +187,12 @@ async function transpiler({ ast, format, mapperFile, includeDocument = true }) {
|
|
|
185
187
|
if (atTarget) {
|
|
186
188
|
const shouldEscapeAt = atTarget.options?.escape !== false;
|
|
187
189
|
let content = node.content;
|
|
188
|
-
if (shouldEscapeAt && (format === htmlFormat
|
|
190
|
+
if (shouldEscapeAt && (format === htmlFormat)) {
|
|
189
191
|
content = escapeHTML(content);
|
|
190
192
|
}
|
|
191
|
-
const rendered = atTarget.render.call(mapperFile, { args: node.args, content, ast: node })
|
|
192
|
-
|
|
193
|
+
const rendered = atTarget.render.call(mapperFile, { args: node.args, content, ast: node });
|
|
194
|
+
const finalAtRendered = (format === mdxFormat) ? rendered : rendered.trimEnd() + "\n";
|
|
195
|
+
output = output.trim() ? output.trimEnd() + "\n" + finalAtRendered : output + finalAtRendered;
|
|
193
196
|
}
|
|
194
197
|
break;
|
|
195
198
|
}
|
package/mappers/languages/mdx.js
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import Mapper from "../mapper.js";
|
|
2
2
|
import MARKDOWN from "./markdown.js";
|
|
3
3
|
import { HTML_TAGS } from "../../constants/html_tags.js";
|
|
4
|
-
import { HTML_PROPS } from "../../constants/html_props.js";
|
|
5
4
|
import { VOID_ELEMENTS } from "../../constants/void_elements.js";
|
|
6
|
-
import kebabize from "../../helpers/kebabize.js";
|
|
7
5
|
|
|
8
6
|
class MdxMapper extends Mapper {
|
|
9
7
|
constructor() {
|
|
@@ -90,7 +88,14 @@ const { tag } = MDX;
|
|
|
90
88
|
MDX.inherit(MARKDOWN);
|
|
91
89
|
|
|
92
90
|
// Block for raw MDX content (ESM, etc.)
|
|
93
|
-
MDX.register("mdx", ({ content }) =>
|
|
91
|
+
MDX.register("mdx", ({ content }) => {
|
|
92
|
+
// Clean up hidden characters
|
|
93
|
+
let clean = content.replace(/\u200B/g, "").trim();
|
|
94
|
+
// Remove leading semicolon to avoid MDX errors
|
|
95
|
+
if (clean.startsWith(";")) clean = clean.substring(1).trim();
|
|
96
|
+
// Add spacing around ESM blocks
|
|
97
|
+
return "\n" + clean + "\n\n";
|
|
98
|
+
}, { escape: false, type: ["AtBlock", "Block"] });
|
|
94
99
|
|
|
95
100
|
// Re-register HTML tags to use jsxProps
|
|
96
101
|
HTML_TAGS.forEach(tagName => {
|
|
@@ -122,6 +127,7 @@ HTML_TAGS.forEach(tagName => {
|
|
|
122
127
|
return element.body(content);
|
|
123
128
|
},
|
|
124
129
|
{
|
|
130
|
+
escape: false,
|
|
125
131
|
type: VOID_ELEMENTS.has(tagName) ? "Block" : ["Block", "Inline"],
|
|
126
132
|
rules: {
|
|
127
133
|
is_self_closing: VOID_ELEMENTS.has(tagName)
|
package/package.json
CHANGED