Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Output deprecation warning when use dollar prefix in global directive #183

Merged
merged 11 commits into from
Aug 11, 2019
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,19 @@

## [Unreleased]

### Added

- Documentation of [custom directives](https://marpit.marp.app/directives?id=custom-directives) ([#183](https://github.com/marp-team/marpit/pull/183))

### Changed

- Allow aliasing from custom directive to built-in directives ([#183](https://github.com/marp-team/marpit/pull/183))
- Upgrade dependent packages to the latest version ([#184](https://github.com/marp-team/marpit/pull/184))

### Deprecated

- Dollar prefix for global directive ([#182](https://github.com/marp-team/marpit/issues/182), [#183](https://github.com/marp-team/marpit/pull/183))

## v1.3.0 - 2019-07-11

### Added
Expand Down
68 changes: 61 additions & 7 deletions docs/directives.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,19 +30,15 @@ paginate: true
---
```

?> Please not confuse to the ruler for paging slides. The actual slide contents would start after the ending ruler of front-matter.
Please not confuse to the ruler for paging slides. The actual slide contents would start after the ending ruler of front-matter.

## Type of directives

### Global directives {docsify-ignore}

Global directives are _the setting value of the whole slide deck_, like `theme`. Marpit recognizes only the last value if you wrote a same global directives many times.

You may use prefix `$` to the name of global directives for clarity type.

```markdown
<!-- $theme: default -->
```
!> We used to support `$` prefix for global directives (`<!-- $theme: default -->`), but it has marked as deprecated in v1.3.1. Developer may re-define dollar-prefixed [custom directives](#custom-directives) as an alias to built-in directive if necessary.

### Local directives {docsify-ignore}

Expand All @@ -65,7 +61,7 @@ If you want to apply local directives only to current page, you have to use pref
```markdown
<!-- _backgroundColor: aqua -->

Add underbar prefix `_` to the name of local directives.
Add underscore prefix `_` to the name of local directives.

---

Expand Down Expand Up @@ -320,3 +316,61 @@ In addition, we have supported customize for these declarations:
- `color`

?> It also can use [extended image syntax](/image-syntax#slide-backgrounds) if you want to set image or color as background to single page.

## Advanced

### Custom directives

Developer can extend recognizable directives. For example, [Marp Core](https://github.com/marp-team/marp-core) has extended `size` global directive to change slide size in Markdown. [Marp CLI](https://github.com/marp-team/marp-cli) will add directives for setting [meta properties of converted HTML](https://github.com/marp-team/marp-cli#metadata).

Marpit instance has [`customDirectives.global` and `customDirectives.local` object](https://marpit-api.marp.app/marpit#customDirectives) to allow adding directives as you like.

#### Custom global directive

The following example is defining dollar-prefixed alias of built-in [`theme` global directive](#theme).

```javascript
marpit.customDirectives.global.$theme = (value, marpit) => {
return { theme: value }
}
```

Please define a function to handle passed value from Markdown. The first argument is the passed value(s), and the second is the current Marpit instance. It should return an object includes pairs of key-value for passing to same kind directives.

#### Custom local directive

Custom directives also can provide a way of assigning multiple same kind directives at once. Let's define `colorPreset` local directive for assigning preset of slide colors.

```javascript
marpit.customDirectives.local.colorPreset = (value, marpit) => {
switch (value) {
case 'sunset':
return { backgroundColor: '#e62e00', color: '#fffff2' }
case 'dark':
return { backgroundColor: '#303033', color: '#f8f8ff' }
default:
// Return an empty object if not have to assign new values
return {}
}
}
```

Now you can use the defined `colorPreset` local directive with same way of built-in local directives. The underscore prefix (`_colorPreset`) for applying preset to single slide also works well.

```markdown
<!-- colorPreset: sunset -->

# Sunset color preset

---

<!-- _colorPreset: dark -->

# Dark color preset

---

# Sunset color preset
```

?> The returned key-value will assign to `marpitDirectives` property in [`meta` object](https://markdown-it.github.io/markdown-it/#Token.prototype.meta) of predetermined markdown-it token(s) by the kind of directive. It would be useful for using assigned value in [markdown-it plugin](./usage.md#extend-marpit-by-plugins).
11 changes: 4 additions & 7 deletions src/markdown/directives/directives.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,11 @@
* Each global directive assigns to the whole slide deck. If you wrote a same
* directive many times, Marpit only recognizes the last value.
*
* You can use prefix `$` as the name of a directive for the clarity (or
* compatibility with the old version of Marp).
*
* @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.
*/
export const globals = {
export const globals = Object.assign(Object.create(null), {
headingDivider: value => {
const headings = [1, 2, 3, 4, 5, 6]
const toInt = v =>
Expand All @@ -44,7 +41,7 @@ export const globals = {
},
style: v => ({ style: v }),
theme: (v, marpit) => (marpit.themeSet.has(v) ? { theme: v } : {}),
}
})

/**
* Local directives.
Expand All @@ -71,7 +68,7 @@ export const globals = {
* a `<header>` element to the first of each slide contents.
* @prop {Directive} paginate Show page number on the slide if you set `true`.
*/
export const locals = {
export const locals = Object.assign(Object.create(null), {
backgroundColor: v => ({ backgroundColor: v }),
backgroundImage: v => ({ backgroundImage: v }),
backgroundPosition: v => ({ backgroundPosition: v }),
Expand All @@ -82,6 +79,6 @@ export const locals = {
footer: v => (typeof v === 'string' ? { footer: v } : {}),
header: v => (typeof v === 'string' ? { header: v } : {}),
paginate: v => ({ paginate: (v || '').toLowerCase() === 'true' }),
}
})

export default [...Object.keys(globals), ...Object.keys(locals)]
69 changes: 43 additions & 26 deletions src/markdown/directives/parse.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,14 @@ import yaml from './yaml'
import * as directives from './directives'
import marpitPlugin from '../marpit_plugin'

const isComment = token =>
token.type === 'marpit_comment' && token.meta.marpitParsedDirectives

const markAsParsed = token => {
token.meta = token.meta || {}
token.meta.marpitCommentParsed = 'directive'
}

/**
* Parse Marpit directives and store result to the slide token meta.
*
Expand All @@ -20,6 +28,20 @@ import marpitPlugin from '../marpit_plugin'
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) }
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When aliasing from custom directive to built-in directive, we will re-apply function of built-in directives to allow only valid value.

} else {
ret[prop] = newProps[prop]
}
}

return ret
}

// Front-matter support
const frontMatter = opts.frontMatter === undefined ? true : !!opts.frontMatter
let frontMatterObject = {}
Expand All @@ -45,25 +67,6 @@ function parse(md, opts = {}) {
})
}

const isComment = token =>
token.type === 'marpit_comment' && token.meta.marpitParsedDirectives

const markAsParsed = token => {
token.meta = token.meta || {}
token.meta.marpitCommentParsed = 'directive'
}

const filterBuiltinDirective = newProps => {
const ret = {}

for (const prop of Object.keys(newProps).filter(
p => !directives.default.includes(p)
))
ret[prop] = newProps[prop]

return ret
}

// Parse global directives
md.core.ruler.after('inline', 'marpit_directives_global_parse', state => {
if (state.inlineMode) return
Expand All @@ -73,7 +76,18 @@ function parse(md, opts = {}) {
let recognized = false

for (const key of Object.keys(obj)) {
const globalKey = key.startsWith('$') ? key.slice(1) : key
const globalKey = key.startsWith('$')
? (() => {
if (marpit.customDirectives.global[key]) return key
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We use an original directive key without warning if the dollar-prefixed custom global directive was defined.


console.warn(
`Deprecation warning: Dollar prefix support for global directive "${key}" is deprecated and will remove soon. Just remove "$" from "${key}" to fix ("${key.slice(
1
)}").`
)
return key.slice(1)
})()
: key

if (directives.globals[globalKey]) {
recognized = true
Expand All @@ -85,8 +99,9 @@ function parse(md, opts = {}) {
recognized = true
globalDirectives = {
...globalDirectives,
...filterBuiltinDirective(
marpit.customDirectives.global[globalKey](obj[key], marpit)
...applyBuiltinDirectives(
marpit.customDirectives.global[globalKey](obj[key], marpit),
directives.globals
),
}
}
Expand Down Expand Up @@ -135,8 +150,9 @@ function parse(md, opts = {}) {
recognized = true
cursor.local = {
...cursor.local,
...filterBuiltinDirective(
marpit.customDirectives.local[key](obj[key], marpit)
...applyBuiltinDirectives(
marpit.customDirectives.local[key](obj[key], marpit),
directives.locals
),
}
}
Expand All @@ -156,8 +172,9 @@ function parse(md, opts = {}) {
recognized = true
cursor.spot = {
...cursor.spot,
...filterBuiltinDirective(
marpit.customDirectives.local[spotKey](obj[key], marpit)
...applyBuiltinDirectives(
marpit.customDirectives.local[spotKey](obj[key], marpit),
directives.locals
),
}
}
Expand Down
9 changes: 6 additions & 3 deletions src/marpit.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ class Marpit {
* value of options after creating instance.
*
* @member {Object} options
* @memberOf Marpit
* @memberOf Marpit#
* @readonly
*/
Object.defineProperty(this, 'options', {
Expand All @@ -82,11 +82,14 @@ class Marpit {
* token.
*
* @member {Object} customDirectives
* @memberOf Marpit
* @memberOf Marpit#
* @readonly
*/
Object.defineProperty(this, 'customDirectives', {
value: Object.seal({ global: {}, local: {} }),
value: Object.seal({
global: Object.create(null),
local: Object.create(null),
}),
})

/**
Expand Down
2 changes: 1 addition & 1 deletion test/markdown/directives/parse.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ describe('Marpit directives parse plugin', () => {
expect(marpitStub.lastGlobalDirectives).toStrictEqual({})
})

it('allows global directive name prefixed "$"', () => {
it('allows global directive name prefixed "$" [DEPRECATED]', () => {
md().parse('<!-- $theme: test_theme -->')
expect(marpitStub.lastGlobalDirectives).toStrictEqual(expected)
})
Expand Down
28 changes: 24 additions & 4 deletions test/marpit.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,15 +92,35 @@ describe('Marpit', () => {
expect(token.meta.marpitDirectives).toStrictEqual({ class: 'ok' })
})

it('cannot assign built-in directive as meta', () => {
it('can assign built-in directive as alias', () => {
const $theme = jest.fn(v => ({ theme: v }))
const marpit = new Marpit({ container: undefined })

marpit.themeSet.add('/* @theme foobar */')
marpit.customDirectives.global.$theme = $theme
marpit.customDirectives.local.test = v => ({ test: v, class: v })

const [first, , , second] = marpit.markdown.parse(
// Global directive (Dollar prefix)
marpit.markdown.render('<!-- $theme: foobar -->')
expect($theme).toBeCalledWith('foobar', marpit)
expect(marpit.lastGlobalDirectives.theme).toBe('foobar')

marpit.markdown.render('<!-- $theme: unknown -->')
expect($theme).toBeCalledWith('unknown', marpit)
expect(marpit.lastGlobalDirectives.theme).toBeUndefined()

// Local directive (Alias + internal meta)
const [localFirst, , , localSecond] = marpit.markdown.parse(
'<!-- test: local -->\n***\n<!-- _test: spot -->'
)
expect(first.meta.marpitDirectives).toStrictEqual({ test: 'local' })
expect(second.meta.marpitDirectives).toStrictEqual({ test: 'spot' })
expect(localFirst.meta.marpitDirectives).toStrictEqual({
test: 'local',
class: 'local',
})
expect(localSecond.meta.marpitDirectives).toStrictEqual({
test: 'spot',
class: 'spot',
})
})

context('with looseYAML option as true', () => {
Expand Down