@podlite/schema 0.0.39 → 0.0.41

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.
@@ -0,0 +1,2 @@
1
+ import { ConfigItem } from '../types';
2
+ export declare function parseAttributes(src: string): ConfigItem[];
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
5
+ }) : (function(o, m, k, k2) {
6
+ if (k2 === undefined) k2 = k;
7
+ o[k2] = m[k];
8
+ }));
9
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
10
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
11
+ }) : function(o, v) {
12
+ o["default"] = v;
13
+ });
14
+ var __importStar = (this && this.__importStar) || function (mod) {
15
+ if (mod && mod.__esModule) return mod;
16
+ var result = {};
17
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
18
+ __setModuleDefault(result, mod);
19
+ return result;
20
+ };
21
+ Object.defineProperty(exports, "__esModule", { value: true });
22
+ exports.parseAttributes = void 0;
23
+ const parser = __importStar(require("../grammar"));
24
+ function parseAttributes(src) {
25
+ if (!src || !src.trim())
26
+ return [];
27
+ try {
28
+ return parser.parse(src, { startRule: 'attributesOnly' });
29
+ }
30
+ catch {
31
+ return [];
32
+ }
33
+ }
34
+ exports.parseAttributes = parseAttributes;
35
+ //# sourceMappingURL=parseAttributes.js.map
package/lib/index.d.ts CHANGED
@@ -11,6 +11,7 @@ export { makeInterator } from './ast-inerator';
11
11
  export { makeTransformer, isNamedBlock, isSemanticBlock } from './helpers/makeTransformer';
12
12
  export { toAny } from './exportAny';
13
13
  export { makeAttrs } from './helpers/config';
14
+ export { parseAttributes } from './helpers/parseAttributes';
14
15
  export { pluginCleanLocation } from './plugin-clean-location';
15
16
  export { toAnyRules } from './helpers/plugins';
16
17
  export { podlitePluggable, Podlite, PodliteExport, cleanIds, frozenIds } from './pluggableParser';
package/lib/index.js CHANGED
@@ -25,7 +25,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
25
25
  return (mod && mod.__esModule) ? mod : { "default": mod };
26
26
  };
27
27
  Object.defineProperty(exports, "__esModule", { value: true });
28
- exports.version = exports.runSelector = exports.parseSelector = exports.Writer = exports.toMarkdown = exports.toHtml = exports.parse = exports.toTree = exports.isValidateError = exports.validateAst = exports.validateAstTree = exports.validatePodliteAst = exports.getPodContentFromNode = exports.getTextContentFromNode = exports.toAst = exports.frozenIds = exports.cleanIds = exports.podlitePluggable = exports.toAnyRules = exports.pluginCleanLocation = exports.makeAttrs = exports.toAny = exports.isSemanticBlock = exports.isNamedBlock = exports.makeTransformer = exports.makeInterator = void 0;
28
+ exports.version = exports.runSelector = exports.parseSelector = exports.Writer = exports.toMarkdown = exports.toHtml = exports.parse = exports.toTree = exports.isValidateError = exports.validateAst = exports.validateAstTree = exports.validatePodliteAst = exports.getPodContentFromNode = exports.getTextContentFromNode = exports.toAst = exports.frozenIds = exports.cleanIds = exports.podlitePluggable = exports.toAnyRules = exports.pluginCleanLocation = exports.parseAttributes = exports.makeAttrs = exports.toAny = exports.isSemanticBlock = exports.isNamedBlock = exports.makeTransformer = exports.makeInterator = void 0;
29
29
  const ajv_1 = __importDefault(require("ajv"));
30
30
  const pointer = __importStar(require("json-pointer"));
31
31
  const jsonShemes = __importStar(require("../schema"));
@@ -45,6 +45,8 @@ var exportAny_1 = require("./exportAny");
45
45
  Object.defineProperty(exports, "toAny", { enumerable: true, get: function () { return exportAny_1.toAny; } });
46
46
  var config_1 = require("./helpers/config");
47
47
  Object.defineProperty(exports, "makeAttrs", { enumerable: true, get: function () { return config_1.makeAttrs; } });
48
+ var parseAttributes_1 = require("./helpers/parseAttributes");
49
+ Object.defineProperty(exports, "parseAttributes", { enumerable: true, get: function () { return parseAttributes_1.parseAttributes; } });
48
50
  var plugin_clean_location_1 = require("./plugin-clean-location");
49
51
  Object.defineProperty(exports, "pluginCleanLocation", { enumerable: true, get: function () { return plugin_clean_location_1.pluginCleanLocation; } });
50
52
  var plugins_1 = require("./helpers/plugins");
@@ -47,33 +47,20 @@ const middle = () => tree => {
47
47
  content: visiter(n.content, ctx, visiter),
48
48
  };
49
49
  const conf = (0, config_1.default)(n, ctx);
50
- const isCodeBlock = 'name' in n && n.name === 'code';
51
- const isDataBlock = 'name' in n && n.name === 'data';
52
- const isMarkdownBlock = 'name' in n && n.name === 'markdown';
53
- const isPictureBlock = 'name' in n && n.name === 'picture';
54
- const isFormulaBlock = 'name' in n && n.name === 'formula';
55
- const allowValues = [...conf.getAllValues('allow'), ...(isCodeBlock ? ['NONE'] : [])];
56
- if ((0, makeTransformer_2.isNamedBlock)(n.name))
50
+ const name = 'name' in n ? n.name : '';
51
+ // Blocks whose content is verbatim by default fcode parsing only
52
+ // kicks in when :allow opts in (per spec, "Formatting within code blocks").
53
+ const isVerbatimDefault = ['code', 'data', 'markdown', 'picture', 'formula'].includes(name);
54
+ const allowValues = conf.getAllValues('allow');
55
+ if ((0, makeTransformer_2.isNamedBlock)(name))
57
56
  return n;
58
- // for code block not parse content by default
59
- if ((isCodeBlock || isDataBlock || isMarkdownBlock || isPictureBlock || isFormulaBlock) &&
60
- allowValues.length == 0)
57
+ if (isVerbatimDefault && allowValues.length === 0)
61
58
  return n;
62
59
  const allowed = allowValues.sort();
63
60
  const transformer = (0, makeTransformer_1.default)({
64
- ':verbatim': (n, ctx) => {
65
- // special case for code block, 'NONE' - flag disabled all markup codes
66
- // do not parse content by default
67
- if (allowed.length == 1 && allowed.includes('NONE'))
68
- return n;
69
- return fcparser.parse(n.value, { allowed });
70
- },
71
- ':text': (n, ctx) => {
72
- return fcparser.parse(n.value, { allowed });
73
- },
74
- ':block': (n, ctx) => {
75
- return transformerBlocks(n, { ...ctx });
76
- },
61
+ ':verbatim': (n, ctx) => fcparser.parse(n.value, { allowed }),
62
+ ':text': (n, ctx) => fcparser.parse(n.value, { allowed }),
63
+ ':block': (n, ctx) => transformerBlocks(n, { ...ctx }),
77
64
  });
78
65
  return { ...n, content: transformer(n.content, { ...ctx }) };
79
66
  },
@@ -81,6 +81,36 @@ function parseTsv(text) {
81
81
  const rows = lines.map(line => line.split('\t'));
82
82
  return rows.filter(r => !(r.length === 1 && r[0].trim() === ''));
83
83
  }
84
+ // Parse a MIME type value into the bare type and a map of parameters
85
+ // (RFC 6838 / RFC 7231 §3.1.1.1). Used to recognise the `header` parameter
86
+ // (RFC 4180 §3) on `:mime-type` of CSV/TSV =data blocks.
87
+ //
88
+ // Examples:
89
+ // "text/csv" → { type: 'text/csv', params: {} }
90
+ // "text/csv; header=present" → { type: 'text/csv', params: { header: 'present' } }
91
+ // "text/csv;charset=utf-8;header=absent" → { ..., params: { charset: 'utf-8', header: 'absent' } }
92
+ function parseMimeType(raw) {
93
+ if (!raw || typeof raw !== 'string')
94
+ return { type: '', params: {} };
95
+ const parts = raw.split(';').map(s => s.trim());
96
+ const type = (parts.shift() || '').toLowerCase();
97
+ const params = {};
98
+ for (const p of parts) {
99
+ if (!p)
100
+ continue;
101
+ const eq = p.indexOf('=');
102
+ if (eq < 0)
103
+ continue;
104
+ const key = p.slice(0, eq).trim().toLowerCase();
105
+ let value = p.slice(eq + 1).trim();
106
+ if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))) {
107
+ value = value.slice(1, -1);
108
+ }
109
+ if (key)
110
+ params[key] = value;
111
+ }
112
+ return { type, params };
113
+ }
84
114
  // Locate the first `=data` block with a matching `:key` attribute anywhere
85
115
  // in the document tree.
86
116
  function findDataBlockByKey(tree, key) {
@@ -161,15 +191,16 @@ function buildRowBlock(cells, isHeader) {
161
191
  }
162
192
  return block;
163
193
  }
164
- // Convert parsed CSV/TSV rows to plain `=row`/`=cell` blocks. The spec is
165
- // silent on how to mark a header row in `=table data:<key>` references, so
166
- // no row receives `:header` here. Authors who need a header row can use a
167
- // structured table with explicit `=begin row :header`, or a Markdown GFM
168
- // table with a separator line.
169
- function csvToTableContent(csvRows) {
170
- return csvRows.map(row => {
194
+ // Convert parsed CSV/TSV rows to plain `=row`/`=cell` blocks. When
195
+ // `hasHeader` is true (signalled by the `header=present` MIME parameter on
196
+ // the source `=data` block, per RFC 4180 §3), the first row is marked with
197
+ // `:header`. Default behaviour leaves all rows unmarked, matching authors
198
+ // who use a structured table with explicit `=begin row :header` or a
199
+ // Markdown GFM table.
200
+ function csvToTableContent(csvRows, hasHeader = false) {
201
+ return csvRows.map((row, i) => {
171
202
  const cells = row.map(v => buildCellBlock(v.trim()));
172
- return buildRowBlock(cells, false);
203
+ return buildRowBlock(cells, hasHeader && i === 0);
173
204
  });
174
205
  }
175
206
  // ─── Error recovery (design notes Rules 2-4) ───────────────────────────────
@@ -418,7 +449,8 @@ exports.default = () => tree => {
418
449
  console.warn(`[table] no =data block found for data:${ref.target} — rendered as empty`);
419
450
  return { ...node, content: [] };
420
451
  }
421
- const mimeType = (0, config_1.default)(dataBlock, {}).getFirstValue('mime-type');
452
+ const rawMime = (0, config_1.default)(dataBlock, {}).getFirstValue('mime-type');
453
+ const { type: mimeType, params: mimeParams } = parseMimeType(rawMime);
422
454
  const isCsv = mimeType === 'text/csv';
423
455
  const isTsv = mimeType === 'text/tab-separated-values';
424
456
  if (isCsv || isTsv) {
@@ -428,11 +460,12 @@ exports.default = () => tree => {
428
460
  console.warn(`[table] ${isCsv ? 'CSV' : 'TSV'} parse produced no rows for data:${ref.target} — rendered as empty`);
429
461
  return { ...node, content: [] };
430
462
  }
431
- const filledNode = { ...node, content: csvToTableContent(rows) };
463
+ const hasHeader = mimeParams.header === 'present';
464
+ const filledNode = { ...node, content: csvToTableContent(rows, hasHeader) };
432
465
  return normalizeCellCounts(filledNode, `table data:${ref.target}`);
433
466
  }
434
467
  // Rule 4: source not tabular → render as code block so content remains visible
435
- console.warn(`[table] =data :key<${ref.target}> has non-tabular mime-type ${mimeType || '(none)'} — rendered as =code`);
468
+ console.warn(`[table] =data :key<${ref.target}> has non-tabular mime-type ${rawMime || '(none)'} — rendered as =code`);
436
469
  return buildCodeFromDataBlock(node, dataBlock);
437
470
  }
438
471
  // structured mode: transform row children (wrap implicit cells), then
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@podlite/schema",
3
- "version": "0.0.39",
3
+ "version": "0.0.41",
4
4
  "description": "AST tools and schema for Podlite markup language — validate, traverse, and transform document trees",
5
5
  "main": "lib/index.js",
6
6
  "homepage": "https://podlite.org",
@@ -40,7 +40,7 @@
40
40
  "build": "run-s build:pegjs build_lib build:ast ",
41
41
  "build_lib": "yarn g:build",
42
42
  "build:ast": "ts-node scripts/make-ast-scheme.ts",
43
- "build:pegjs": "pegmill -o src/grammar.js src/grammar.pegjs && pegmill -o src/grammarfc.js src/grammarfc.pegjs",
43
+ "build:pegjs": "pegmill --allowed-start-rules Document,attributesOnly -o src/grammar.js src/grammar.pegjs && pegmill -o src/grammarfc.js src/grammarfc.pegjs",
44
44
  "watch": "npx nodemon -e js,ts --exec 'yarn' build",
45
45
  "watch:pegjs": "npx nodemon -e pegmill --exec 'yarn' build:pegjs",
46
46
  "test": "yarn g:jest --passWithNoTests"