1
0

add initial marp implementation with sample content and build configuration

This commit is contained in:
2025-09-13 18:13:22 +02:00
parent dcacc9b409
commit e5f219507f
10319 changed files with 1402023 additions and 0 deletions

View File

@@ -0,0 +1,98 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = exports.apply = void 0;
var _lodash = _interopRequireDefault(require("lodash.kebabcase"));
var _inline_style = _interopRequireDefault(require("../../helpers/inline_style"));
var _plugin = _interopRequireDefault(require("../../plugin"));
var _directives = _interopRequireDefault(require("./directives"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
/** @module */
/**
* Apply parsed Marpit directives to markdown-it tokens.
*
* @function apply
* @param {MarkdownIt} md markdown-it instance.
* @param {Object} [opts]
* @param {boolean} [opts.dataset=true] Assigns directives as HTML data
* attributes of each section tag.
* @param {boolean} [opts.css=true] Assigns directives as CSS Custom Properties
* of each section tag.
*/
function _apply(md, opts = {}) {
const {
marpit
} = md;
const {
lang
} = marpit.options;
const dataset = opts.dataset === undefined ? true : !!opts.dataset;
const css = opts.css === undefined ? true : !!opts.css;
const {
global,
local
} = marpit.customDirectives;
const directives = [...Object.keys(global), ...Object.keys(local), ..._directives.default];
md.core.ruler.after('marpit_directives_parse', 'marpit_directives_apply', state => {
if (state.inlineMode) return;
let pageNumber = 0;
const tokensForPaginationTotal = [];
for (const token of state.tokens) {
const {
marpitDirectives
} = token.meta || {};
if (token.type === 'marpit_slide_open') {
// `skip` and `hold` disable increment of the page number
if (!(marpitDirectives?.paginate === 'skip' || marpitDirectives?.paginate === 'hold')) {
pageNumber += 1;
}
}
if (marpitDirectives) {
const style = new _inline_style.default(token.attrGet('style'));
for (const dir of Object.keys(marpitDirectives)) {
if (directives.includes(dir)) {
const value = marpitDirectives[dir];
if (value) {
const kebabCaseDir = (0, _lodash.default)(dir);
if (dataset) token.attrSet(`data-${kebabCaseDir}`, value);
if (css) style.set(`--${kebabCaseDir}`, value);
}
}
}
// Apply attribute to token
if (marpitDirectives.lang || lang) token.attrSet('lang', marpitDirectives.lang || lang);
if (marpitDirectives.class) token.attrJoin('class', marpitDirectives.class);
if (marpitDirectives.color) style.set('color', marpitDirectives.color);
if (marpitDirectives.backgroundColor) style.set('background-color', marpitDirectives.backgroundColor).set('background-image', 'none');
if (marpitDirectives.backgroundImage) {
style.set('background-image', marpitDirectives.backgroundImage).set('background-position', 'center').set('background-repeat', 'no-repeat').set('background-size', 'cover');
if (marpitDirectives.backgroundPosition) style.set('background-position', marpitDirectives.backgroundPosition);
if (marpitDirectives.backgroundRepeat) style.set('background-repeat', marpitDirectives.backgroundRepeat);
if (marpitDirectives.backgroundSize) style.set('background-size', marpitDirectives.backgroundSize);
}
if (marpitDirectives.paginate && marpitDirectives.paginate !== 'skip') {
// If the page number was still not incremented, mark this page as
// the first page.
if (pageNumber <= 0) pageNumber = 1;
token.attrSet('data-marpit-pagination', pageNumber);
tokensForPaginationTotal.push(token);
}
if (marpitDirectives.header) token.meta.marpitHeader = marpitDirectives.header;
if (marpitDirectives.footer) token.meta.marpitFooter = marpitDirectives.footer;
const styleStr = style.toString();
if (styleStr !== '') token.attrSet('style', styleStr);
}
}
// Set total page number to each slide page that has pagination attribute
for (const token of tokensForPaginationTotal) {
token.attrSet('data-marpit-pagination-total', pageNumber);
}
});
}
const apply = exports.apply = (0, _plugin.default)(_apply);
var _default = exports.default = apply;

View File

@@ -0,0 +1,126 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.locals = exports.globals = exports.default = void 0;
/**
* The definition of Marpit directives
* @module
*/
/**
* @typedef {Function} Directive
* @param {string} value Parsed value.
* @param {Marpit} marpit Marpit instance.
* @returns {Object} Assigning object to token meta.
*/
/**
* Global directives.
*
* Each global directive assigns to the whole slide deck. If you wrote a same
* directive many times, Marpit only recognizes the last value.
*
* @prop {Directive} headingDivider Specify heading divider option.
* @prop {Directive} style Specify the CSS style to apply additionally.
* @prop {Directive} theme Specify theme of the slide deck.
* @prop {Directive} lang Specify the language of the slide deck. It will
* assign as `lang` attribute for each slide.
*/
const globals = exports.globals = Object.assign(Object.create(null), {
headingDivider: value => {
const headings = [1, 2, 3, 4, 5, 6];
const toInt = v => Array.isArray(v) || Number.isNaN(v) ? v : Number.parseInt(v, 10);
const converted = toInt(value);
if (Array.isArray(converted)) {
const convertedArr = converted.map(toInt);
return {
headingDivider: headings.filter(v => convertedArr.includes(v))
};
}
if (value === 'false') return {
headingDivider: false
};
if (headings.includes(converted)) return {
headingDivider: converted
};
return {};
},
style: v => ({
style: v
}),
theme: (v, marpit) => marpit.themeSet.has(v) ? {
theme: v
} : {},
lang: v => ({
lang: v
})
});
/**
* Local directives.
*
* Mainly these are used to change settings each slide page. By default, a
* local directive applies to the defined page and followed pages.
*
* If you want to set a local directive to single page only, you can add the
* prefix `_` (underbar) to directive name. (Spot directives)
*
* @prop {Directive} backgroundColor Specify background-color style.
* @prop {Directive} backgroundImage Specify background-image style.
* @prop {Directive} backgroundPosition Specify background-position style. The
* default value while setting backgroundImage is `center`.
* @prop {Directive} backgroundRepeat Specify background-repeat style. The
* default value while setting backgroundImage is `no-repeat`.
* @prop {Directive} backgroundSize Specify background-size style. The default
* value while setting backgroundImage is `cover`.
* @prop {Directive} class Specify HTML class of section element(s).
* @prop {Directive} color Specify color style (base text color).
* @prop {Directive} footer Specify the content of slide footer. It will insert
* a `<footer>` element to the last of each slide contents.
* @prop {Directive} header Specify the content of slide header. It will insert
* a `<header>` element to the first of each slide contents.
* @prop {Directive} paginate Show page number on the slide if you set `true`.
* `hold` and `skip` are also available to disable increment of the page
* number.
*/
const locals = exports.locals = Object.assign(Object.create(null), {
backgroundColor: v => ({
backgroundColor: v
}),
backgroundImage: v => ({
backgroundImage: v
}),
backgroundPosition: v => ({
backgroundPosition: v
}),
backgroundRepeat: v => ({
backgroundRepeat: v
}),
backgroundSize: v => ({
backgroundSize: v
}),
class: v => ({
class: Array.isArray(v) ? v.join(' ') : v
}),
color: v => ({
color: v
}),
footer: v => typeof v === 'string' ? {
footer: v
} : {},
header: v => typeof v === 'string' ? {
header: v
} : {},
paginate: v => {
const normalized = (v || '').toLowerCase();
if (['hold', 'skip'].includes(normalized)) return {
paginate: normalized
};
return {
paginate: normalized === 'true'
};
}
});
var _default = exports.default = [...Object.keys(globals), ...Object.keys(locals)];

View File

@@ -0,0 +1,182 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.parse = exports.default = void 0;
var _markdownItFrontMatter = _interopRequireDefault(require("markdown-it-front-matter"));
var _plugin = _interopRequireDefault(require("../../plugin"));
var _comment = require("../comment");
var directives = _interopRequireWildcard(require("./directives"));
var _yaml = require("./yaml");
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
/** @module */
const isDirectiveComment = token => token.type === 'marpit_comment' && token.meta.marpitParsedDirectives;
/**
* Parse Marpit directives and store result to the slide token meta.
*
* Marpit comment plugin ans slide plugin requires already loaded to
* markdown-it instance.
*
* @function parse
* @param {MarkdownIt} md markdown-it instance.
* @param {Object} [opts]
* @param {boolean} [opts.frontMatter=true] Switch feature to support YAML
* front-matter. If true, you can use Jekyll style directive setting to the
* first page.
*/
function _parse(md, opts = {}) {
const {
marpit
} = md;
const applyBuiltinDirectives = (newProps, builtinDirectives) => {
let ret = {};
for (const prop of Object.keys(newProps)) {
if (builtinDirectives[prop]) {
ret = {
...ret,
...builtinDirectives[prop](newProps[prop], marpit)
};
} else {
ret[prop] = newProps[prop];
}
}
return ret;
};
// Front-matter support
const frontMatter = opts.frontMatter === undefined ? true : !!opts.frontMatter;
let frontMatterObject = {};
if (frontMatter) {
md.core.ruler.before('block', 'marpit_directives_front_matter', state => {
frontMatterObject = {};
if (!state.inlineMode) marpit.lastGlobalDirectives = {};
});
md.use(_markdownItFrontMatter.default, fm => {
frontMatterObject.text = fm;
const parsed = (0, _yaml.yaml)(fm, marpit.options.looseYAML ? [...Object.keys(marpit.customDirectives.global), ...Object.keys(marpit.customDirectives.local)] : false);
if (parsed !== false) frontMatterObject.yaml = parsed;
});
}
// Parse global directives
md.core.ruler.after('inline', 'marpit_directives_global_parse', state => {
if (state.inlineMode) return;
let globalDirectives = {};
const applyDirectives = obj => {
let recognized = false;
for (const key of Object.keys(obj)) {
if (directives.globals[key]) {
recognized = true;
globalDirectives = {
...globalDirectives,
...directives.globals[key](obj[key], marpit)
};
} else if (marpit.customDirectives.global[key]) {
recognized = true;
globalDirectives = {
...globalDirectives,
...applyBuiltinDirectives(marpit.customDirectives.global[key](obj[key], marpit), directives.globals)
};
}
}
return recognized;
};
if (frontMatterObject.yaml) applyDirectives(frontMatterObject.yaml);
for (const token of state.tokens) {
if (isDirectiveComment(token) && applyDirectives(token.meta.marpitParsedDirectives)) {
(0, _comment.markAsParsed)(token, 'directive');
} else if (token.type === 'inline') {
for (const t of token.children) {
if (isDirectiveComment(t) && applyDirectives(t.meta.marpitParsedDirectives)) (0, _comment.markAsParsed)(t, 'directive');
}
}
}
marpit.lastGlobalDirectives = {
...globalDirectives
};
});
// Parse local directives and apply meta to slide
md.core.ruler.after('marpit_slide', 'marpit_directives_parse', state => {
if (state.inlineMode) return;
const slides = [];
const cursor = {
slide: undefined,
local: {},
spot: {}
};
const applyDirectives = obj => {
let recognized = false;
for (const key of Object.keys(obj)) {
if (directives.locals[key]) {
recognized = true;
cursor.local = {
...cursor.local,
...directives.locals[key](obj[key], marpit)
};
} else if (marpit.customDirectives.local[key]) {
recognized = true;
cursor.local = {
...cursor.local,
...applyBuiltinDirectives(marpit.customDirectives.local[key](obj[key], marpit), directives.locals)
};
}
// Spot directives
// (Apply local directive to only current slide by prefix "_")
if (key.startsWith('_')) {
const spotKey = key.slice(1);
if (directives.locals[spotKey]) {
recognized = true;
cursor.spot = {
...cursor.spot,
...directives.locals[spotKey](obj[key], marpit)
};
} else if (marpit.customDirectives.local[spotKey]) {
recognized = true;
cursor.spot = {
...cursor.spot,
...applyBuiltinDirectives(marpit.customDirectives.local[spotKey](obj[key], marpit), directives.locals)
};
}
}
}
return recognized;
};
if (frontMatterObject.yaml) applyDirectives(frontMatterObject.yaml);
for (const token of state.tokens) {
if (token.meta && token.meta.marpitSlideElement === 1) {
// Initialize Marpit directives meta
token.meta.marpitDirectives = {};
slides.push(token);
cursor.slide = token;
} else if (token.meta && token.meta.marpitSlideElement === -1) {
// Assign local and spot directives to meta
cursor.slide.meta.marpitDirectives = {
...cursor.slide.meta.marpitDirectives,
...cursor.local,
...cursor.spot
};
cursor.spot = {};
} else if (isDirectiveComment(token) && applyDirectives(token.meta.marpitParsedDirectives)) {
(0, _comment.markAsParsed)(token, 'directive');
} else if (token.type === 'inline') {
for (const t of token.children) {
if (isDirectiveComment(t) && applyDirectives(t.meta.marpitParsedDirectives)) (0, _comment.markAsParsed)(t, 'directive');
}
}
}
// Assign global directives to meta
for (const token of slides) token.meta.marpitDirectives = {
...token.meta.marpitDirectives,
...marpit.lastGlobalDirectives
};
});
}
const parse = exports.parse = (0, _plugin.default)(_parse);
var _default = exports.default = parse;

View File

@@ -0,0 +1,62 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.yaml = exports.default = void 0;
var _jsYaml = require("js-yaml");
var _directives = _interopRequireDefault(require("./directives"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
/** @module */
const createPatterns = keys => {
const set = new Set();
for (const k of keys) {
const normalized = '_?' + k.replace(/[.*+?^=!:${}()|[\]\\/]/g, '\\$&');
set.add(normalized);
set.add(`"${normalized}"`);
set.add(`'${normalized}'`);
}
return [...set.values()];
};
const yamlSpecialChars = `["'{|>~&*`;
function parse(text) {
try {
const obj = (0, _jsYaml.load)(text, {
schema: _jsYaml.FAILSAFE_SCHEMA
});
if (obj === null || typeof obj !== 'object') return false;
return obj;
} catch {
return false;
}
}
function convertLoose(text, looseDirectives) {
const keyPattern = `(?:${createPatterns(looseDirectives).join('|')})`;
const looseMatcher = new RegExp(`^(${keyPattern}\\s*:)(.+)$`);
let normalized = '';
for (const line of text.split(/\r?\n/)) normalized += `${line.replace(looseMatcher, (original, prop, value) => {
const trimmed = value.trim();
if (trimmed.length === 0 || yamlSpecialChars.includes(trimmed[0])) return original;
const spaceLength = value.length - value.trimLeft().length;
const spaces = value.substring(0, spaceLength);
return `${prop}${spaces}"${trimmed.split('"').join('\\"')}"`;
})}\n`;
return normalized.trim();
}
/**
* Parse text as YAML by using js-yaml's FAILSAFE_SCHEMA.
*
* @function yaml
* @param {String} text Target text.
* @param {boolean|string[]} [looseDirectives=false] By setting `true`, it try
* to parse as loose YAML only in defined Marpit built-in directives. You
* may also extend target keys for loose parsing by passing an array of
* strings.
* @returns {Object|false} Return parse result, or `false` when failed to parse.
*/
const yaml = (text, looseDirectives = false) => parse(looseDirectives ? convertLoose(text, [..._directives.default, ...(Array.isArray(looseDirectives) ? looseDirectives : [])]) : text);
exports.yaml = yaml;
var _default = exports.default = yaml;