├── .editorconfig ├── .eslintrc.js ├── .gitignore ├── .npmignore ├── .travis.yml ├── LICENSE ├── README.md ├── index.js ├── lib ├── css.js ├── html.js └── snippets-registry.js ├── package-lock.json ├── package.json ├── rollup.config.js └── test ├── config.js ├── css.js ├── html.js └── sample-config.json /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = tab 5 | indent_size = 4 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.{json,yml}] 12 | indent_style = space 13 | indent_size = 2 14 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "env": { 3 | "es6": true, 4 | "node": true, 5 | "mocha": true, 6 | "browser": true 7 | }, 8 | "extends": "eslint:recommended", 9 | "parserOptions": { 10 | "sourceType": "module" 11 | }, 12 | "rules": { 13 | "indent": [ 14 | "error", 15 | "tab" 16 | ], 17 | "linebreak-style": [ 18 | "error", 19 | "unix" 20 | ], 21 | "quotes": [ 22 | "error", 23 | "single" 24 | ], 25 | "semi": [ 26 | "error", 27 | "always" 28 | ], 29 | "no-cond-assign": "off", 30 | "no-empty": [ 31 | "error", 32 | { "allowEmptyCatch": true } 33 | ], 34 | "no-console": "warn" 35 | } 36 | }; 37 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | 11 | # Directory for instrumented libs generated by jscoverage/JSCover 12 | lib-cov 13 | 14 | # Coverage directory used by tools like istanbul 15 | coverage 16 | 17 | # nyc test coverage 18 | .nyc_output 19 | 20 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 21 | .grunt 22 | 23 | # node-waf configuration 24 | .lock-wscript 25 | 26 | # Compiled binary addons (http://nodejs.org/api/addons.html) 27 | build/Release 28 | 29 | # Dependency directories 30 | node_modules 31 | jspm_packages 32 | 33 | # Optional npm cache directory 34 | .npm 35 | 36 | # Optional REPL history 37 | .node_repl_history 38 | /dist 39 | .DS_Store 40 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | npm-debug.log* 2 | node_modules 3 | jspm_packages 4 | .npm 5 | /.* 6 | /*.* 7 | /test 8 | /lib 9 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | sudo: false 3 | node_js: 4 | - "4" 5 | - "5" 6 | - "6" 7 | - "7" 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Emmet.io 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Deprecated 2 | 3 | Module implementation is moved to monorepo: https://github.com/emmetio/emmet 4 | 5 | --- 6 | 7 | # [Emmet](http://emmet.io) abbreviation expander 8 | 9 | Reference implementation of Emmet’s “Expand Abbreviation” action. 10 | 11 | ```js 12 | import { expand } from '@emmetio/expand-abbreviation'; 13 | 14 | console.log(expand('ul.nav>.nav-item{Item $}*2')); 15 | // outputs: 16 | // 20 | 21 | // use XHTML-style output 22 | console.log(expand('img[src=image.png]', { 23 | profile: { 24 | selfClosingStyle: 'xhtml' 25 | } 26 | })); 27 | // outputs: 28 | 29 | // Output in Slim syntax 30 | console.log(expand('ul.nav>.nav-item{Item $}*2', {syntax: 'slim'})); 31 | // outputs: 32 | // ul.nav 33 | // li.nav-item Item 1 34 | // li.nav-item Item 2 35 | ``` 36 | 37 | ## API 38 | 39 | This module exports two functions: `parse(abbr, options)` and `expand(abbr, options)`. 40 | 41 | The `parse(abbr, options)` function [parses abbreviation into tree](https://github.com/emmetio/abbreviation), applies various transformations required for proper output and returns parsed tree. The `expand(abbr, options)` does the same but returns formatted string. In most cases you should use `expand(abbr, options)` only but if you want to analyze or update parsed abbreviation somehow, you can `parse()` abbreviation first, update parsed tree and then `expand()` it: 42 | 43 | ```js 44 | import { parse, expand } from '@emmetio/expand-abbreviation'; 45 | 46 | // 1. Parse string abbreviation into tree 47 | const tree = parse('ul>.item*3'); 48 | 49 | // 2. Walk on each tree node, read or update them somehow 50 | tree.walk(node => { ... }); 51 | 52 | // 3. Output result 53 | console.log(expand(tree)); 54 | ``` 55 | 56 | ### Options 57 | 58 | Both `parse()` and `expand()` methods accept the following options: 59 | 60 | * `syntax` (string): abbreviation output syntax. Currently supported syntaxes are: `html`, `slim`, `pug`, `haml`. 61 | * `field(index, placeholder)` (function): field/tabstop generator for host editor. Most editors support TextMate-style fields: `${0}` or `${1:placeholder}`. For TextMate-style fields this function will look like this: 62 | 63 | ```js 64 | const field = (index, placeholder) => `\${${index}${placeholder ? ':' + placeholder : ''}}`; 65 | ``` 66 | 67 | > Emmet natively supports TextMate fields and provides [module for parsing them](https://github.com/emmetio/field-parser). 68 | 69 | * `text` (string or array of strings): insert given text string(s) into expanded abbreviation. If array of strings is given, the implicitly repeated element (e.g. `li*`) will be repeated by the amount of items in array. 70 | * `profile` (object or [`Profile`](https://github.com/emmetio/output-profile)): either predefined [output profile](https://github.com/emmetio/output-profile) or options for output profile. Used for by [markup formatters](https://github.com/emmetio/markup-formatters) to shape-up final output. 71 | * `variables` (object): custom variables for [variable resolver](https://github.com/emmetio/variable-resolver). 72 | * `snippets` (object, array of objects or [`SnippetsRegistry`](https://github.com/emmetio/snippets-registry)): custom predefined snippets for abbreviation. The expanded abbreviation will try to match given snippets that may contain custom elements, predefined attributes etc. May also contain array of items: either snippets (object) or references to [default syntax snippets](https://github.com/emmetio/snippets) (string; the key in default snippets hash). 73 | * `addons` (object): hash of [additional transformations](https://github.com/emmetio/html-transform/tree/master/lib/addons) that should be applied to expanded abbreviation, like BEM or JSX. Since these transformations introduce side-effect, they are disabled by default and should be enabled by providing a transform name as key and transform options as value: 74 | 75 | ```js 76 | { 77 | bem: {element: '--'}, // enable transform & override options 78 | jsx: true // no options, just enable transform 79 | } 80 | ``` 81 | 82 | * `format` (object): additional options for output formatter: 83 | * `markup` (object): options for markup syntaxes like XML, HTML, Pug, Slim etc.: 84 | ```js 85 | // Auto-comment expanded HTML elements with specific attributes, e.g. `p.foo` → `

` 86 | comment: { 87 | // Enable/disable commenting 88 | enabled: false, 89 | 90 | // Attributes that should trigger node commenting on specific node, if commenting is enabled 91 | trigger: ['id', 'class'], 92 | 93 | // Template strings for placing before opening and/or after closing tags. Content between `[` and `]` will be outputted only if specified attribute name (uppercased; dashes replaced with underscores) is available in element 94 | before: '', 95 | after: '\n' 96 | } 97 | ``` 98 | * `stylesheet` (object): options for stylesheet formatters like CSS, SCSS, LESS etc.: 99 | ```js 100 | { 101 | // Use short hex notation where possible, e.g. `#000` instead of `#000000` 102 | shortHex: true, 103 | 104 | // A string between property name and value 105 | between: ': ', 106 | 107 | // A string after property value 108 | after: ';' 109 | } 110 | ``` 111 | 112 | 113 | See [`test`](/test) folder for usage examples. 114 | 115 | ## Design goals 116 | 117 | This module is just an umbrella projects that combines various stand-alone submodules into a unified process for parsing and outputting Emmet abbreviations. Thus, you can create your own “Expand Abbreviation” implementation that can re-use these submodules with additional tweaks and transforms that matches your requirements. 118 | 119 | The standard abbreviation expanding workflow: 120 | 121 | 1. [Parse Emmet abbreviation](https://github.com/emmetio/abbreviation) into DOM-like tree. 122 | 1. [Prepare parsed tree for markup output](https://github.com/emmetio/html-transform). This step includes implicit name resolving (`.item` → `div.item`), item numbering (`.item$*2` → `.item1+.item2`) and so on. 123 | 1. Match tree nodes with [predefined snippets](https://github.com/emmetio/snippets). Snippets are basically another Emmet abbreviations that define element shape (name, attributes, self-closing etc.). 124 | 1. [Resolve variables](https://github.com/emmetio/variable-resolver) in parsed tree. 125 | 1. Convert parsed abbreviation to formatted string using [markup formatters](https://github.com/emmetio/markup-formatters). 126 | 127 | ## Build targets 128 | 129 | `@emmetio/expand-abbreviation` NPM module is available in two flavors: CommonJS and ES6 modules. There’s also a complete, zero-dependency UMD module suitable for browsers (see `dist/expand-full.js`). 130 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import Profile from '@emmetio/output-profile'; 4 | import SnippetsRegistry from '@emmetio/snippets-registry'; 5 | import { expand as htmlExpand, parse as htmlParse } from './lib/html'; 6 | import { expand as cssExpand, parse as cssParse } from './lib/css'; 7 | import snippetsRegistryFactory from './lib/snippets-registry'; 8 | 9 | /** 10 | * Default variables used in snippets to insert common values into predefined snippets 11 | * @type {Object} 12 | */ 13 | const defaultVariables = { 14 | lang: 'en', 15 | locale: 'en-US', 16 | charset: 'UTF-8' 17 | }; 18 | 19 | /** 20 | * A list of syntaxes that should use Emmet CSS abbreviations: 21 | * a variations of default abbreviation that holds values right in abbreviation name 22 | * @type {Array} 23 | */ 24 | const stylesheetSyntaxes = ['css', 'sass', 'scss', 'less', 'stylus', 'sss']; 25 | 26 | const defaultOptions = { 27 | /** 28 | * Type of abbreviation to parse: 'markup' or 'stylesheet'. 29 | * Can be auto-detected from `syntax` property. Default is 'markup' 30 | */ 31 | type: null, 32 | 33 | /** 34 | * Abbreviation output syntax 35 | * @type {String} 36 | */ 37 | syntax: 'html', 38 | 39 | /** 40 | * Field/tabstop generator for editor. Most editors support TextMate-style 41 | * fields: ${0} or ${1:item}. So for TextMate-style fields this function 42 | * will look like this: 43 | * @example 44 | * (index, placeholder) => `\${${index}${placeholder ? ':' + placeholder : ''}}` 45 | * 46 | * @param {Number} index Placeholder index. Fields with the same indices 47 | * should be linked 48 | * @param {String} [placeholder] Field placeholder 49 | * @return {String} 50 | */ 51 | field: (index, placeholder) => placeholder || '', 52 | 53 | /** 54 | * Insert given text string(s) into expanded abbreviation 55 | * If array of strings is given, the implicitly repeated element (e.g. `li*`) 56 | * will be repeated by the amount of items in array 57 | * @type {String|String[]} 58 | */ 59 | text: null, 60 | 61 | /** 62 | * Either predefined output profile or options for output profile. Used for 63 | * abbreviation output 64 | * @type {Profile|Object} 65 | */ 66 | profile: null, 67 | 68 | /** 69 | * Custom variables for variable resolver 70 | * @see @emmetio/variable-resolver 71 | * @type {Object} 72 | */ 73 | variables: {}, 74 | 75 | /** 76 | * Custom predefined snippets for abbreviation. The expanded abbreviation 77 | * will try to match given snippets that may contain custom elements, 78 | * predefined attributes etc. 79 | * May also contain array of items: either snippets (Object) or references 80 | * to default syntax snippets (String; the key in default snippets hash) 81 | * @see @emmetio/snippets 82 | * @type {Object|SnippetsRegistry} 83 | */ 84 | snippets: {}, 85 | 86 | /** 87 | * Hash of additional transformations that should be applied to expanded 88 | * abbreviation, like BEM or JSX. Since these transformations introduce 89 | * side-effect, they are disabled by default and should be enabled by 90 | * providing a transform name as a key and transform options as value: 91 | * @example 92 | * { 93 | * bem: {element: '--'}, 94 | * jsx: true // no options, just enable transform 95 | * } 96 | * @see @emmetio/html-transform/lib/addons 97 | * @type {Object} 98 | */ 99 | options: null, 100 | 101 | /** 102 | * Additional options for syntax formatter 103 | * @see @emmetio/markup-formatters 104 | * @type {Object} 105 | */ 106 | format: null 107 | }; 108 | 109 | /** 110 | * Expands given abbreviation into string, formatted according to provided 111 | * syntax and options 112 | * @param {String|Node} abbr Abbreviation string or parsed abbreviation tree 113 | * @param {String|Object} [config] Parsing and formatting options (object) or 114 | * abbreviation syntax (string) 115 | * @return {String} 116 | */ 117 | export function expand(abbr, config) { 118 | config = createOptions(config); 119 | 120 | return getType(config.type, config.syntax) === 'stylesheet' 121 | ? cssExpand(abbr, config) 122 | : htmlExpand(abbr, config); 123 | } 124 | 125 | /** 126 | * Parses given abbreviation into AST tree. This tree can be later formatted to 127 | * string with `expand` function 128 | * @param {String} abbr Abbreviation to parse 129 | * @param {String|Object} [options] Parsing and formatting options (object) or 130 | * abbreviation syntax (string) 131 | * @return {Node} 132 | */ 133 | export function parse(abbr, options) { 134 | options = createOptions(options); 135 | 136 | return getType(options.type, options.syntax) === 'stylesheet' 137 | ? cssParse(abbr, options) 138 | : htmlParse(abbr, options); 139 | } 140 | 141 | /** 142 | * Creates snippets registry for given syntax and additional `snippets` 143 | * @param {String} type Abbreviation type, 'markup' or 'stylesheet' 144 | * @param {String} syntax Snippets syntax, used for retrieving predefined snippets 145 | * @param {SnippetsRegistry|Object|Object[]} [snippets] Additional snippets 146 | * @return {SnippetsRegistry} 147 | */ 148 | export function createSnippetsRegistry(type, syntax, snippets) { 149 | // Backward-compatibility with <0.6 150 | if (type && type !== 'markup' && type !== 'stylesheet') { 151 | snippets = syntax; 152 | syntax = type; 153 | type = 'markup'; 154 | } 155 | 156 | return snippets instanceof SnippetsRegistry 157 | ? snippets 158 | : snippetsRegistryFactory(type, syntax, snippets); 159 | } 160 | 161 | export function createOptions(options) { 162 | if (typeof options === 'string') { 163 | options = { syntax: options }; 164 | } 165 | 166 | options = Object.assign({}, defaultOptions, options); 167 | if (options.type == null && options.syntax) { 168 | options.type = isStylesheet(options.syntax) ? 'stylesheet' : 'markup'; 169 | } 170 | 171 | options.format = Object.assign({field: options.field}, options.format); 172 | options.profile = createProfile(options); 173 | options.variables = Object.assign({}, defaultVariables, options.variables); 174 | options.snippets = createSnippetsRegistry(options.type, options.syntax, options.snippets); 175 | 176 | return options; 177 | } 178 | 179 | /** 180 | * Check if given syntax belongs to stylesheet markup. 181 | * Emmet uses different abbreviation flavours: one is a default markup syntax, 182 | * used for HTML, Slim, Pug etc, the other one is used for stylesheets and 183 | * allows embedded values in abbreviation name 184 | * @param {String} syntax 185 | * @return {Boolean} 186 | */ 187 | export function isStylesheet(syntax) { 188 | return stylesheetSyntaxes.indexOf(syntax) !== -1; 189 | } 190 | 191 | /** 192 | * Creates output profile from given options 193 | * @param {Object} options 194 | * @return {Profile} 195 | */ 196 | export function createProfile(options) { 197 | return options.profile instanceof Profile 198 | ? options.profile 199 | : new Profile(options.profile); 200 | } 201 | 202 | /** 203 | * Returns type of abbreviation expander: either 'markup' or 'stylesheet' 204 | * @param {String} type 205 | * @param {String} [syntax] 206 | */ 207 | function getType(type, syntax) { 208 | if (type) { 209 | return type === 'stylesheet' ? 'stylesheet' : 'markup'; 210 | } 211 | 212 | return isStylesheet(syntax) ? 'stylesheet' : 'markup'; 213 | } 214 | -------------------------------------------------------------------------------- /lib/css.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import parseAbbreviation from '@emmetio/css-abbreviation'; 4 | import resolveSnippets from '@emmetio/css-snippets-resolver'; 5 | import format from '@emmetio/stylesheet-formatters'; 6 | 7 | /** 8 | * Expands given abbreviation into code 9 | * @param {String|Node} abbr Abbreviation to parse or already parsed abbreviation 10 | * @param {Object} config 11 | * @return {String} 12 | */ 13 | export function expand(abbr, config) { 14 | config = config || {}; 15 | 16 | if (typeof abbr === 'string') { 17 | abbr = parse(abbr, config); 18 | } 19 | 20 | return format(abbr, config.profile, config.syntax, config); 21 | } 22 | 23 | /** 24 | * Parses given Emmet abbreviation into a final abbreviation tree with all 25 | * required transformations applied 26 | * @param {String|Node} abbr Abbreviation to parse or already parsed abbreviation 27 | * @param {Object} config 28 | * @return {Node} 29 | */ 30 | export function parse(abbr, config) { 31 | if (typeof abbr === 'string') { 32 | abbr = parseAbbreviation(abbr); 33 | } 34 | 35 | return abbr.use(resolveSnippets, config.snippets, config.options); 36 | } 37 | -------------------------------------------------------------------------------- /lib/html.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import parseAbbreviation from '@emmetio/abbreviation'; 4 | import resolveSnippets from '@emmetio/html-snippets-resolver'; 5 | import transform from '@emmetio/html-transform'; 6 | import resolveVariables from '@emmetio/variable-resolver'; 7 | import format from '@emmetio/markup-formatters'; 8 | 9 | /** 10 | * Expands given abbreviation into code 11 | * @param {String|Node} abbr Abbreviation to parse or already parsed abbreviation 12 | * @param {Object} config 13 | * @return {String} 14 | */ 15 | export function expand(abbr, config) { 16 | config = Object.assign({}, config); 17 | 18 | if (typeof abbr === 'string') { 19 | abbr = parse(abbr, config); 20 | } 21 | 22 | return format(abbr, config.profile, config.syntax, config); 23 | } 24 | 25 | /** 26 | * Parses given Emmet abbreviation into a final abbreviation tree with all 27 | * required transformations applied 28 | * @param {String} Abbreviation to parse 29 | * @param {Object} config 30 | * @return {Node} 31 | */ 32 | export function parse(abbr, config) { 33 | return parseAbbreviation(abbr) 34 | .use(resolveSnippets, config.snippets) 35 | .use(resolveVariables, config.variables) 36 | .use(transform, config.text, config.options); 37 | } 38 | -------------------------------------------------------------------------------- /lib/snippets-registry.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import defaultSnippets from '@emmetio/snippets'; 4 | import lorem from '@emmetio/lorem'; 5 | import SnippetsRegistry from '@emmetio/snippets-registry'; 6 | 7 | const reLorem = /^lorem([a-z]*)(\d*)$/i; 8 | 9 | /** 10 | * Constructs a snippets registry, filled with snippets, for given options 11 | * @param {String} syntax Abbreviation syntax 12 | * @param {Object|Object[]} snippets Additional snippets 13 | * @return {SnippetsRegistry} 14 | */ 15 | export default function(type, syntax, snippets) { 16 | const registrySnippets = []; 17 | 18 | if (type === 'markup') { 19 | registrySnippets.push(defaultSnippets.html); 20 | } else if (type === 'stylesheet') { 21 | registrySnippets.push(defaultSnippets.css); 22 | } 23 | 24 | if (syntax in defaultSnippets && registrySnippets.indexOf(defaultSnippets[syntax]) === -1) { 25 | registrySnippets.push(defaultSnippets[syntax]); 26 | } 27 | 28 | if (Array.isArray(snippets)) { 29 | snippets.forEach(item => { 30 | // if array item is a string, treat it as a reference to globally 31 | // defined snippets 32 | registrySnippets.push(typeof item === 'string' ? defaultSnippets[item] : item); 33 | }); 34 | } else if (typeof snippets === 'object') { 35 | registrySnippets.push(snippets); 36 | } 37 | 38 | const registry = new SnippetsRegistry(registrySnippets.filter(Boolean)); 39 | 40 | // for non-stylesheet syntaxes add Lorem Ipsum generator 41 | if (type !== 'stylesheet') { 42 | registry.get(0).set(reLorem, loremGenerator); 43 | } 44 | 45 | return registry; 46 | } 47 | 48 | function loremGenerator(node) { 49 | const options = {}; 50 | const m = node.name.match(reLorem); 51 | if (m[1]) { 52 | options.lang = m[1]; 53 | } 54 | 55 | if (m[2]) { 56 | options.wordCount = +m[2]; 57 | } 58 | 59 | return lorem(node, options); 60 | } 61 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@emmetio/expand-abbreviation", 3 | "version": "0.7.3", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@emmetio/abbreviation": { 8 | "version": "0.7.0", 9 | "resolved": "https://registry.npmjs.org/@emmetio/abbreviation/-/abbreviation-0.7.0.tgz", 10 | "integrity": "sha512-CjLWtUCyh2nRgG/TkBruPTNI03i+b2Bau9UP7g0zgWdeV6bBjNC8CxX106ZdMekK3I/RmTrWuzVV9b+7nwqKXA==", 11 | "requires": { 12 | "@emmetio/node": "^0.1.2", 13 | "@emmetio/stream-reader": "^2.2.0", 14 | "@emmetio/stream-reader-utils": "^0.1.0" 15 | } 16 | }, 17 | "@emmetio/config": { 18 | "version": "0.3.0", 19 | "resolved": "https://registry.npmjs.org/@emmetio/config/-/config-0.3.0.tgz", 20 | "integrity": "sha512-IvWUxjNTy8sl+9ysTQYLRjWzyTrnMFAl+LmKsnu+yRMdnPGrhYABwZwtTk4yrVUPhb0YsX+jQegmt/h14CBV5A==", 21 | "dev": true 22 | }, 23 | "@emmetio/css-abbreviation": { 24 | "version": "0.4.0", 25 | "resolved": "https://registry.npmjs.org/@emmetio/css-abbreviation/-/css-abbreviation-0.4.0.tgz", 26 | "integrity": "sha512-8b4+ZoBElpNMedO+gGCiEX/xGv8Do0NYUvsdVNv6O0fuP9octnxyzjb7HFGqDJ7hFzVORAfzOhpjyL8y2dw9AQ==", 27 | "requires": { 28 | "@emmetio/node": "^0.1.2", 29 | "@emmetio/stream-reader": "^2.2.0", 30 | "@emmetio/stream-reader-utils": "^0.1.0" 31 | } 32 | }, 33 | "@emmetio/css-snippets-resolver": { 34 | "version": "0.4.0", 35 | "resolved": "https://registry.npmjs.org/@emmetio/css-snippets-resolver/-/css-snippets-resolver-0.4.0.tgz", 36 | "integrity": "sha512-H0eOed2KljA/nQ64j3BKwAkAFliku5LAD58o4pvS3A9tZ2g3ctEpJ+61po78SZE1+Q+gRA3CGtC6rxtk7QlGPA==" 37 | }, 38 | "@emmetio/field-parser": { 39 | "version": "0.3.1", 40 | "resolved": "https://registry.npmjs.org/@emmetio/field-parser/-/field-parser-0.3.1.tgz", 41 | "integrity": "sha512-A26JuVvZRUBb/rNpaDdmBB2jaN3spx2JRLJQfkskz9CRbiSW9ZE/M7etKKMunV5UWUfSlygFaFUclT3y+UNDxw==", 42 | "requires": { 43 | "@emmetio/stream-reader": "^2.2.0", 44 | "@emmetio/stream-reader-utils": "^0.1.0" 45 | } 46 | }, 47 | "@emmetio/html-snippets-resolver": { 48 | "version": "0.1.4", 49 | "resolved": "https://registry.npmjs.org/@emmetio/html-snippets-resolver/-/html-snippets-resolver-0.1.4.tgz", 50 | "integrity": "sha1-szrT+nj9eNZLlL+Iqftos62Ojn4=", 51 | "requires": { 52 | "@emmetio/abbreviation": "^0.6.0" 53 | }, 54 | "dependencies": { 55 | "@emmetio/abbreviation": { 56 | "version": "0.6.6", 57 | "resolved": "https://registry.npmjs.org/@emmetio/abbreviation/-/abbreviation-0.6.6.tgz", 58 | "integrity": "sha512-jsh1Hyc7iY+5tADcn6GlIF/kxEbglPW+JE/FcCb4NNYqDr2swXvEGWd+DN3porBA67VDfDZVAwThhvYTsHKrRw==", 59 | "requires": { 60 | "@emmetio/node": "^0.1.2", 61 | "@emmetio/stream-reader": "^2.2.0", 62 | "@emmetio/stream-reader-utils": "^0.1.0" 63 | } 64 | } 65 | } 66 | }, 67 | "@emmetio/html-transform": { 68 | "version": "0.3.9", 69 | "resolved": "https://registry.npmjs.org/@emmetio/html-transform/-/html-transform-0.3.9.tgz", 70 | "integrity": "sha512-Eoj+eebHCMFnqsH2ZNYcDTJIwUg1rmLIitTH89ZqU7kMyS0Z8NuB6QHjxOozF2xQAGryl8CZCbeBcGGn4OwpvQ==", 71 | "requires": { 72 | "@emmetio/implicit-tag": "^1.0.0" 73 | } 74 | }, 75 | "@emmetio/implicit-tag": { 76 | "version": "1.0.0", 77 | "resolved": "https://registry.npmjs.org/@emmetio/implicit-tag/-/implicit-tag-1.0.0.tgz", 78 | "integrity": "sha1-+CbU4fxR2jlDTCMmtvbQ4rLntBk=" 79 | }, 80 | "@emmetio/lorem": { 81 | "version": "1.0.2", 82 | "resolved": "https://registry.npmjs.org/@emmetio/lorem/-/lorem-1.0.2.tgz", 83 | "integrity": "sha1-jealcY85Fy6n0iUypbDTu9EAg9w=", 84 | "requires": { 85 | "@emmetio/implicit-tag": "^1.0.0" 86 | } 87 | }, 88 | "@emmetio/markup-formatters": { 89 | "version": "0.4.1", 90 | "resolved": "https://registry.npmjs.org/@emmetio/markup-formatters/-/markup-formatters-0.4.1.tgz", 91 | "integrity": "sha512-RR+QPozAAL7pnFL5Nl3g5qXxUF2qw54oGl0njmBTZYYwf96LDJ2p6DfRhy8uCenRjMMgUijjQ31wtmDR4cobDA==", 92 | "requires": { 93 | "@emmetio/field-parser": "^0.3.0", 94 | "@emmetio/output-renderer": "^0.1.2" 95 | } 96 | }, 97 | "@emmetio/node": { 98 | "version": "0.1.2", 99 | "resolved": "https://registry.npmjs.org/@emmetio/node/-/node-0.1.2.tgz", 100 | "integrity": "sha1-QjHVOMRUgaUYNfwquj9dn8EpY0w=" 101 | }, 102 | "@emmetio/output-profile": { 103 | "version": "0.1.6", 104 | "resolved": "https://registry.npmjs.org/@emmetio/output-profile/-/output-profile-0.1.6.tgz", 105 | "integrity": "sha512-7bIwR3YHmTEnyy76X4l9e5+wXE+lah2E1AIoeDyKFIfVujztXNqcv+BmPcV4LRl1+V6Qir8hIex+F2nz7PTPCg==" 106 | }, 107 | "@emmetio/output-renderer": { 108 | "version": "0.1.2", 109 | "resolved": "https://registry.npmjs.org/@emmetio/output-renderer/-/output-renderer-0.1.2.tgz", 110 | "integrity": "sha1-Ds4RrM6SmFB4aK7SGrUlPh6wgvg=", 111 | "requires": { 112 | "@emmetio/field-parser": "^0.3.0" 113 | } 114 | }, 115 | "@emmetio/snippets": { 116 | "version": "0.2.9", 117 | "resolved": "https://registry.npmjs.org/@emmetio/snippets/-/snippets-0.2.9.tgz", 118 | "integrity": "sha512-n/oaJMcKB5No8Q1wnxfHmrZV2LA40cmYMwVbH0ebfpiZSxX1EMY26QaYu+PJiGcqR+GElS79aMagIO+dOTCntw==" 119 | }, 120 | "@emmetio/snippets-registry": { 121 | "version": "0.3.1", 122 | "resolved": "https://registry.npmjs.org/@emmetio/snippets-registry/-/snippets-registry-0.3.1.tgz", 123 | "integrity": "sha1-7A6KEi/paDZZzmmiI5b0E2uUDSA=" 124 | }, 125 | "@emmetio/stream-reader": { 126 | "version": "2.2.0", 127 | "resolved": "https://registry.npmjs.org/@emmetio/stream-reader/-/stream-reader-2.2.0.tgz", 128 | "integrity": "sha1-Rs/+oRmgoAMxKiHC2bVijLX81EI=" 129 | }, 130 | "@emmetio/stream-reader-utils": { 131 | "version": "0.1.0", 132 | "resolved": "https://registry.npmjs.org/@emmetio/stream-reader-utils/-/stream-reader-utils-0.1.0.tgz", 133 | "integrity": "sha1-JEywLHfsLnT3ipvTGCGKvJxQCmE=" 134 | }, 135 | "@emmetio/stylesheet-formatters": { 136 | "version": "0.2.1", 137 | "resolved": "https://registry.npmjs.org/@emmetio/stylesheet-formatters/-/stylesheet-formatters-0.2.1.tgz", 138 | "integrity": "sha512-1XHNVx6S3ra7dnv12CNT6Gq15VyT2Fkrhgp2yCj4g2jJJflVHU6t++VfjoK6w36hGYu0AqZL9TCa/8hLj2YjQw==", 139 | "requires": { 140 | "@emmetio/field-parser": "^0.3.0", 141 | "@emmetio/output-renderer": "^0.1.1" 142 | } 143 | }, 144 | "@emmetio/variable-resolver": { 145 | "version": "0.2.1", 146 | "resolved": "https://registry.npmjs.org/@emmetio/variable-resolver/-/variable-resolver-0.2.1.tgz", 147 | "integrity": "sha1-zhG1FYRmmjQJhmkM8OwmnkTGP7o=" 148 | }, 149 | "balanced-match": { 150 | "version": "1.0.0", 151 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 152 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", 153 | "dev": true 154 | }, 155 | "brace-expansion": { 156 | "version": "1.1.8", 157 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", 158 | "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=", 159 | "dev": true, 160 | "requires": { 161 | "balanced-match": "^1.0.0", 162 | "concat-map": "0.0.1" 163 | } 164 | }, 165 | "browser-stdout": { 166 | "version": "1.3.0", 167 | "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.0.tgz", 168 | "integrity": "sha1-81HTKWnTL6XXpVZxVCY9korjvR8=", 169 | "dev": true 170 | }, 171 | "builtin-modules": { 172 | "version": "2.0.0", 173 | "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-2.0.0.tgz", 174 | "integrity": "sha512-3U5kUA5VPsRUA3nofm/BXX7GVHKfxz0hOBAPxXrIvHzlDRkQVqEn6yi8QJegxl4LzOHLdvb7XF5dVawa/VVYBg==", 175 | "dev": true 176 | }, 177 | "commander": { 178 | "version": "2.11.0", 179 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz", 180 | "integrity": "sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ==", 181 | "dev": true 182 | }, 183 | "concat-map": { 184 | "version": "0.0.1", 185 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 186 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", 187 | "dev": true 188 | }, 189 | "diff": { 190 | "version": "3.3.1", 191 | "resolved": "https://registry.npmjs.org/diff/-/diff-3.3.1.tgz", 192 | "integrity": "sha512-MKPHZDMB0o6yHyDryUOScqZibp914ksXwAMYMTHj6KO8UeKsRYNJD3oNCKjTqZon+V488P7N/HzXF8t7ZR95ww==", 193 | "dev": true 194 | }, 195 | "escape-string-regexp": { 196 | "version": "1.0.5", 197 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 198 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", 199 | "dev": true 200 | }, 201 | "fs.realpath": { 202 | "version": "1.0.0", 203 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 204 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", 205 | "dev": true 206 | }, 207 | "glob": { 208 | "version": "7.1.2", 209 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", 210 | "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", 211 | "dev": true, 212 | "requires": { 213 | "fs.realpath": "^1.0.0", 214 | "inflight": "^1.0.4", 215 | "inherits": "2", 216 | "minimatch": "^3.0.4", 217 | "once": "^1.3.0", 218 | "path-is-absolute": "^1.0.0" 219 | } 220 | }, 221 | "growl": { 222 | "version": "1.10.3", 223 | "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.3.tgz", 224 | "integrity": "sha512-hKlsbA5Vu3xsh1Cg3J7jSmX/WaW6A5oBeqzM88oNbCRQFz+zUaXm6yxS4RVytp1scBoJzSYl4YAEOQIt6O8V1Q==", 225 | "dev": true 226 | }, 227 | "has-flag": { 228 | "version": "2.0.0", 229 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", 230 | "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", 231 | "dev": true 232 | }, 233 | "he": { 234 | "version": "1.1.1", 235 | "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", 236 | "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", 237 | "dev": true 238 | }, 239 | "inflight": { 240 | "version": "1.0.6", 241 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 242 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 243 | "dev": true, 244 | "requires": { 245 | "once": "^1.3.0", 246 | "wrappy": "1" 247 | } 248 | }, 249 | "inherits": { 250 | "version": "2.0.3", 251 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 252 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", 253 | "dev": true 254 | }, 255 | "is-module": { 256 | "version": "1.0.0", 257 | "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", 258 | "integrity": "sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE=", 259 | "dev": true 260 | }, 261 | "minimatch": { 262 | "version": "3.0.4", 263 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 264 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 265 | "dev": true, 266 | "requires": { 267 | "brace-expansion": "^1.1.7" 268 | } 269 | }, 270 | "minimist": { 271 | "version": "0.0.8", 272 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", 273 | "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", 274 | "dev": true 275 | }, 276 | "mkdirp": { 277 | "version": "0.5.1", 278 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", 279 | "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", 280 | "dev": true, 281 | "requires": { 282 | "minimist": "0.0.8" 283 | } 284 | }, 285 | "mocha": { 286 | "version": "5.0.1", 287 | "resolved": "https://registry.npmjs.org/mocha/-/mocha-5.0.1.tgz", 288 | "integrity": "sha512-SpwyojlnE/WRBNGtvJSNfllfm5PqEDFxcWluSIgLeSBJtXG4DmoX2NNAeEA7rP5kK+79VgtVq8nG6HskaL1ykg==", 289 | "dev": true, 290 | "requires": { 291 | "browser-stdout": "1.3.0", 292 | "commander": "2.11.0", 293 | "debug": "3.1.0", 294 | "diff": "3.3.1", 295 | "escape-string-regexp": "1.0.5", 296 | "glob": "7.1.2", 297 | "growl": "1.10.3", 298 | "he": "1.1.1", 299 | "mkdirp": "0.5.1", 300 | "supports-color": "4.4.0" 301 | }, 302 | "dependencies": { 303 | "debug": { 304 | "version": "3.1.0", 305 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", 306 | "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", 307 | "dev": true, 308 | "requires": { 309 | "ms": "2.0.0" 310 | } 311 | }, 312 | "supports-color": { 313 | "version": "4.4.0", 314 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.4.0.tgz", 315 | "integrity": "sha512-rKC3+DyXWgK0ZLKwmRsrkyHVZAjNkfzeehuFWdGGcqGDTZFH73+RH6S/RDAAxl9GusSjZSUWYLmT9N5pzXFOXQ==", 316 | "dev": true, 317 | "requires": { 318 | "has-flag": "^2.0.0" 319 | } 320 | } 321 | } 322 | }, 323 | "ms": { 324 | "version": "2.0.0", 325 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 326 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", 327 | "dev": true 328 | }, 329 | "once": { 330 | "version": "1.4.0", 331 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 332 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 333 | "dev": true, 334 | "requires": { 335 | "wrappy": "1" 336 | } 337 | }, 338 | "path-is-absolute": { 339 | "version": "1.0.1", 340 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 341 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", 342 | "dev": true 343 | }, 344 | "path-parse": { 345 | "version": "1.0.5", 346 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz", 347 | "integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME=", 348 | "dev": true 349 | }, 350 | "resolve": { 351 | "version": "1.6.0", 352 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.6.0.tgz", 353 | "integrity": "sha512-mw7JQNu5ExIkcw4LPih0owX/TZXjD/ZUF/ZQ/pDnkw3ZKhDcZZw5klmBlj6gVMwjQ3Pz5Jgu7F3d0jcDVuEWdw==", 354 | "dev": true, 355 | "requires": { 356 | "path-parse": "^1.0.5" 357 | } 358 | }, 359 | "rollup": { 360 | "version": "0.56.3", 361 | "resolved": "https://registry.npmjs.org/rollup/-/rollup-0.56.3.tgz", 362 | "integrity": "sha512-/iH4RfioboHgBjo7TbQcdMad/ifVGY/ToOB1AsW7oZHUhfhm+low6QlrImUSaJO1JqklOpWEKlD+b3MZYLuptA==", 363 | "dev": true 364 | }, 365 | "rollup-plugin-node-resolve": { 366 | "version": "3.3.0", 367 | "resolved": "https://registry.npmjs.org/rollup-plugin-node-resolve/-/rollup-plugin-node-resolve-3.3.0.tgz", 368 | "integrity": "sha512-9zHGr3oUJq6G+X0oRMYlzid9fXicBdiydhwGChdyeNRGPcN/majtegApRKHLR5drboUvEWU+QeUmGTyEZQs3WA==", 369 | "dev": true, 370 | "requires": { 371 | "builtin-modules": "^2.0.0", 372 | "is-module": "^1.0.0", 373 | "resolve": "^1.1.6" 374 | } 375 | }, 376 | "wrappy": { 377 | "version": "1.0.2", 378 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 379 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", 380 | "dev": true 381 | } 382 | } 383 | } 384 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@emmetio/expand-abbreviation", 3 | "version": "0.7.3", 4 | "description": "Reference implementation of Emmet abbreviation expander", 5 | "main": "dist/expand.cjs.js", 6 | "module": "dist/expand.es.js", 7 | "dependencies": { 8 | "@emmetio/abbreviation": "^0.7.0", 9 | "@emmetio/css-abbreviation": "^0.4.0", 10 | "@emmetio/css-snippets-resolver": "^0.4.0", 11 | "@emmetio/html-snippets-resolver": "^0.1.4", 12 | "@emmetio/html-transform": "^0.3.9", 13 | "@emmetio/lorem": "^1.0.1", 14 | "@emmetio/markup-formatters": "^0.4.1", 15 | "@emmetio/output-profile": "^0.1.6", 16 | "@emmetio/snippets": "^0.2.9", 17 | "@emmetio/snippets-registry": "^0.3.1", 18 | "@emmetio/stylesheet-formatters": "^0.2.1", 19 | "@emmetio/variable-resolver": "^0.2.1" 20 | }, 21 | "devDependencies": { 22 | "@emmetio/config": "^0.3.0", 23 | "mocha": "^5.0.1", 24 | "rollup": "^0.56.3", 25 | "rollup-plugin-node-resolve": "^3.2.0" 26 | }, 27 | "scripts": { 28 | "test": "mocha", 29 | "build": "rollup -c", 30 | "pretest": "NODE_ENV=test npm run build", 31 | "prepare": "npm run test && npm run build" 32 | }, 33 | "repository": { 34 | "type": "git", 35 | "url": "git+https://github.com/emmetio/expand-abbreviation.git" 36 | }, 37 | "keywords": [ 38 | "emmet", 39 | "abbreviation", 40 | "expand" 41 | ], 42 | "author": "Sergey Chikuyonok ", 43 | "license": "MIT", 44 | "bugs": { 45 | "url": "https://github.com/emmetio/expand-abbreviation/issues" 46 | }, 47 | "homepage": "https://github.com/emmetio/expand-abbreviation#readme" 48 | } 49 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import nodeResolve from 'rollup-plugin-node-resolve'; 4 | 5 | const config = [{ 6 | input: './index.js', 7 | external: [ 8 | '@emmetio/abbreviation', 9 | '@emmetio/css-abbreviation', 10 | '@emmetio/css-snippets-resolver', 11 | '@emmetio/stylesheet-formatters', 12 | '@emmetio/lorem', 13 | '@emmetio/snippets', 14 | '@emmetio/snippets-registry', 15 | '@emmetio/html-snippets-resolver', 16 | '@emmetio/output-profile', 17 | '@emmetio/html-transform', 18 | '@emmetio/variable-resolver', 19 | '@emmetio/markup-formatters' 20 | ], 21 | output: [{ 22 | format: 'cjs', 23 | sourcemap: true, 24 | file: 'dist/expand.cjs.js' 25 | }, { 26 | format: 'es', 27 | sourcemap: true, 28 | file: 'dist/expand.es.js' 29 | }] 30 | }]; 31 | 32 | if (process.env.NODE_ENV !== 'test') { 33 | config.push({ 34 | input: './index.js', 35 | plugins: [nodeResolve()], 36 | output: { 37 | format: 'umd', 38 | name: 'emmet', 39 | sourcemap: true, 40 | file: 'dist/expand-full.js', 41 | } 42 | }); 43 | } 44 | 45 | export default config; 46 | -------------------------------------------------------------------------------- /test/config.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const config = require('@emmetio/config'); 5 | const expand = require('../').expand; 6 | const sampleConfig = require('./sample-config.json'); 7 | 8 | describe('Config support', () => { 9 | describe('Markup', () => { 10 | it('globals', () => { 11 | const conf = config(sampleConfig, { syntax: 'html' }); 12 | 13 | assert.equal( 14 | expand('.test', conf), 15 | '
\n', 16 | 'Use markup format and profile options' 17 | ); 18 | 19 | assert.equal( 20 | expand('.foo>.-bar', conf), 21 | '
\n\t
\n\t\n
\n', 22 | 'Use markup BEM options' 23 | ); 24 | 25 | assert.equal( 26 | expand('[lang="${lang}" foo=${bar}]', conf), 27 | '
', 28 | 'Use markup variables' 29 | ); 30 | 31 | assert.equal( 32 | expand('sw>a>img', conf), 33 | '', 34 | 'Use markup & default snippets' 35 | ); 36 | }); 37 | 38 | it('syntax', () => { 39 | const conf = config(sampleConfig, { syntax: 'angular' }); 40 | 41 | assert.equal( 42 | expand('.test', conf), 43 | '
', 44 | 'Use markup format and profile options, override with syntax format & options' 45 | ); 46 | 47 | assert.equal( 48 | expand('sw>a>img', conf), 49 | '
', 50 | 'Use markup snippets, override with syntax snippets' 51 | ); 52 | }); 53 | 54 | it('project', () => { 55 | const conf = config(sampleConfig, { 56 | syntax: 'angular', 57 | project: 'proj1' 58 | }); 59 | 60 | // NB even if `tagCase` is set to "upper", syntax-specific config 61 | // takes precedence over type-specific, defined in project 62 | assert.equal( 63 | expand('.foo>.-bar', conf), 64 | '
', 65 | 'Use markup format & syntax config, disable formatting in project' 66 | ); 67 | 68 | assert.equal( 69 | expand('p1s>sw>a>img', conf), 70 | '
', 71 | 'Use markup & syntax snippets, add from project globals' 72 | ); 73 | }); 74 | }); 75 | 76 | describe('Stylesheet', () => { 77 | it('globals', () => { 78 | const conf = config(sampleConfig, { type: 'stylesheet' }); 79 | 80 | assert.equal( 81 | expand('c', conf), 82 | 'COLOR:#000;', 83 | 'Use stylesheet format and profile options' 84 | ); 85 | 86 | assert.equal( 87 | expand('c+bg#fc0', conf), 88 | 'COLOR:#000;BACKGROUND:#fc0;', 89 | 'Use stylesheet format and profile options' 90 | ); 91 | 92 | assert.equal( 93 | expand('p10+gd', conf), 94 | 'PADDING:10px;GRID:;', 95 | 'Use stylesheet & default snippets' 96 | ); 97 | }); 98 | 99 | it('syntax', () => { 100 | const conf = config(sampleConfig, { syntax: 'sugarss' }); 101 | 102 | assert.equal( 103 | expand('c', conf), 104 | 'COLOR #000', 105 | 'Use stylesheet format and profile options, override with syntax' 106 | ); 107 | 108 | assert.equal( 109 | expand('c+bg#fc0', conf), 110 | 'COLOR #000BACKGROUND #fc0', 111 | 'Use stylesheet format and profile options, override with syntax' 112 | ); 113 | 114 | assert.equal( 115 | expand('p10+gd+foo', conf), 116 | 'PADDING 10pxGRID BAR ', 117 | 'Use stylesheet & default snippets, override with syntax' 118 | ); 119 | }); 120 | 121 | it('project', () => { 122 | const conf = config(sampleConfig, { 123 | syntax: 'sugarss', 124 | project: 'proj1' 125 | }); 126 | 127 | assert.equal( 128 | expand('c', conf), 129 | 'COLOR #000;;', 130 | 'Use global & syntax format and profile options, override with project' 131 | ); 132 | 133 | assert.equal( 134 | expand('c+bg#fc0', conf), 135 | 'COLOR #000;;BACKGROUND #fc0;;', 136 | 'Use global & syntax format and profile options, override with project' 137 | ); 138 | 139 | assert.equal( 140 | expand('gd+foo', conf), 141 | 'BAZ ;;BAR ;;', 142 | 'Use global & syntax snippets, override with project' 143 | ); 144 | 145 | assert.equal( 146 | expand('p10+m5a', conf), 147 | 'PADDING 10cm;;MARGIN 5bbbb;;', 148 | 'Override options with project' 149 | ); 150 | }); 151 | }); 152 | }); 153 | -------------------------------------------------------------------------------- /test/css.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const _expand = require('../').expand; 5 | 6 | const expand = (abbr, options) => _expand(abbr, Object.assign({syntax: 'css'}, options)); 7 | 8 | describe('CSS expand', () => { 9 | it('basic', () => { 10 | assert.equal(expand('p10'), 'padding: 10px;'); 11 | assert.equal(expand('c'), 'color: #000;'); 12 | assert.equal(expand('fl-l'), 'float: left;'); 13 | assert.equal(expand('fl-r'), 'float: right;'); 14 | assert.equal(expand('@k'), '@keyframes identifier {\n\t\n}'); 15 | assert.equal(expand('fll'), 'float: left;'); 16 | assert.equal(expand('ovh'), 'overflow: hidden;'); 17 | 18 | // insert TextMate-style fields/tabstops in output 19 | const field = (index, placeholder) => `\${${index}${placeholder ? ':' + placeholder : ''}}`; 20 | assert.equal(expand('bg-u', {field}), 'background: url(${1});'); 21 | assert.equal(expand('c', {field}), 'color: #${2:000};'); 22 | }); 23 | 24 | it('linear-gradient', () => { 25 | assert.equal(expand('lg(to right, black, #f00.4)'), 'background-image: linear-gradient(to right, black, rgba(255, 0, 0, 0.4));'); 26 | }); 27 | 28 | it('chains', () => { 29 | assert.equal(expand('p.5+m-a'), 'padding: 0.5em;\nmargin: auto;'); 30 | }); 31 | 32 | it('formatter options', () => { 33 | let options = { 34 | format: { 35 | between: '::', 36 | after: ';;' 37 | }, 38 | options: { 39 | intUnit: 'pt', 40 | floatUnit: 'vh', 41 | unitAliases: { 42 | e: 'em', 43 | p: '%', 44 | x: 'ex', 45 | r: ' / @rem' 46 | } 47 | } 48 | }; 49 | assert.equal(expand('p10', options), 'padding::10pt;;'); 50 | assert.equal(expand('p10.2', options), 'padding::10.2vh;;'); 51 | assert.equal(expand('p10r', options), 'padding::10 / @rem;;'); 52 | }); 53 | }); 54 | -------------------------------------------------------------------------------- /test/html.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const expand = require('../').expand; 5 | 6 | describe('HTML expand', () => { 7 | it('basic', () => { 8 | assert.equal(expand('ul>.item$*2'), ''); 9 | 10 | // insert text into abbreviation 11 | assert.equal(expand('ul>.item$*', {text: ['foo', 'bar']}), ''); 12 | 13 | // insert TextMate-style fields/tabstops in output 14 | const field = (index, placeholder) => `\${${index}${placeholder ? ':' + placeholder : ''}}`; 15 | assert.equal(expand('ul>.item$*2', {field}), ''); 16 | }); 17 | 18 | it('syntax', () => { 19 | assert.equal(expand('ul>.item$*2', {syntax: 'html'}), ''); 20 | assert.equal(expand('ul>.item$*2', {syntax: 'slim'}), 'ul\n\tli.item1\n\tli.item2'); 21 | assert.equal(expand('xsl:variable[name=a select=b]>div', { options: { xsl: true } }), '\n\t
\n
'); 22 | }); 23 | 24 | it('custom profile', () => { 25 | const profile = {selfClosingStyle: 'xhtml'}; 26 | 27 | assert.equal(expand('img'), ''); 28 | assert.equal(expand('img', {profile}), ''); 29 | }); 30 | 31 | it('custom variables', () => { 32 | const variables = {charset: 'ru-RU'}; 33 | 34 | assert.equal(expand('[charset=${charset}]{${charset}}'), '
UTF-8
'); 35 | assert.equal(expand('[charset=${charset}]{${charset}}', {variables}), '
ru-RU
'); 36 | }); 37 | 38 | it('custom snippets', () => { 39 | const snippets = { 40 | link: 'link[foo=bar href]/', 41 | 'foo': '.foo[bar=baz]' 42 | }; 43 | 44 | assert.equal(expand('foo', { snippets }), '
'); 45 | 46 | // `link:css` depends on `link` snippet so changing it will result in 47 | // altered `link:css` result 48 | assert.equal(expand('link:css'), ''); 49 | assert.equal(expand('link:css', { snippets }), ''); 50 | }); 51 | 52 | it('formatter options', () => { 53 | const format = {comment: {enabled: true}}; 54 | 55 | assert.equal(expand('ul>.item$*2'), ''); 56 | assert.equal(expand('ul>.item$*2', {format}), ''); 57 | }); 58 | 59 | it('lorem ipsum', () => { 60 | const reLorem = /^Lorem.+ipsum.+dolor/; 61 | assert(reLorem.test(expand('lorem'))); 62 | assert(reLorem.test(expand('LoRem'))); 63 | assert(reLorem.test(expand('LORem20'))); 64 | }); 65 | }); 66 | -------------------------------------------------------------------------------- /test/sample-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "globals": { 4 | "markup": { 5 | "format": { 6 | "comment": { 7 | "enabled": true 8 | } 9 | }, 10 | "profile": { 11 | "tagCase": "upper", 12 | "attributeCase": "upper" 13 | }, 14 | "options": { 15 | "bem": { 16 | "element": "_el", 17 | "modifier": "-mod" 18 | } 19 | }, 20 | "variables": { 21 | "lang": "ru", 22 | "foo": "bar" 23 | }, 24 | "snippets": { 25 | "a": "a[href title]", 26 | "list": "ul>li.item" 27 | } 28 | }, 29 | "stylesheet": { 30 | "format": { 31 | "shortHex": true, 32 | "between": ":" 33 | }, 34 | "profile": { 35 | "tagCase": "upper", 36 | "format": false 37 | }, 38 | "snippets": { 39 | "gd": "grid" 40 | } 41 | } 42 | }, 43 | "syntax": { 44 | "angular": { 45 | "type": "markup", 46 | "format": { 47 | "comment": { 48 | "enabled": false 49 | } 50 | }, 51 | "options": { 52 | "jsx": true 53 | }, 54 | "profile": { 55 | "tagCase": "lower", 56 | "attributeCase": "upper" 57 | }, 58 | "snippets": { 59 | "sw": "div[ngSwitch]" 60 | } 61 | }, 62 | "sugarss": { 63 | "type": "stylesheet", 64 | "format": { 65 | "between": " ", 66 | "after": "" 67 | }, 68 | "snippets": { 69 | "foo": "bar" 70 | } 71 | } 72 | }, 73 | "project": { 74 | "proj1": { 75 | "globals": { 76 | "markup": { 77 | "profile": { 78 | "tagCase": "upper", 79 | "format": false 80 | }, 81 | "snippets": { 82 | "p1s": "div.proj1-snippet" 83 | } 84 | }, 85 | "stylesheet": { 86 | "snippets": { 87 | "gd": "baz" 88 | } 89 | } 90 | }, 91 | "syntax": { 92 | "sugarss": { 93 | "format": { 94 | "after": ";;" 95 | }, 96 | "options": { 97 | "intUnit": "cm", 98 | "unitAliases": { 99 | "a": "bbbb" 100 | } 101 | } 102 | } 103 | } 104 | } 105 | } 106 | } 107 | --------------------------------------------------------------------------------