diff --git a/.editorconfig b/.editorconfig index fbb9538..ac1eb01 100644 --- a/.editorconfig +++ b/.editorconfig @@ -6,15 +6,12 @@ root = true [*] charset = utf-8 -indent_size = 4 +indent_size = 2 end_of_line = lf indent_style = space trim_trailing_whitespace = true insert_final_newline = true -[*.{bemjson.js,deps.js}] -indent_size = 4 - [{bower,package}.json] indent_size = 2 diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..0579417 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +* @voischev @GitScrum @mrmlnc @michael-ciniawsky diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md new file mode 100644 index 0000000..710ba0e --- /dev/null +++ b/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,29 @@ +> ✏ī¸ Briefly describe the issue you are experiencing (or the feature you want to see added to the plugin). Tell us what you were trying to do and what happened instead. Remember, this is _not_ a place to ask questions. For that, go to http://gitter.im/posthtml/posthtml + +### `Details` + +> ✏ī¸ Describe in more detail the problem you have been experiencing, if necessary. + +## `Error (Logs|Stacks)` + +> 👉 Create a [gist](https://gist.github.com) which is a paste of your **full** logs, and link them here. + +> ⚠ī¸ Do **not** paste your full logs here (or at least hide them by using a `
` block), as it will make this issue long and hard to read! If you are reporting a bug, **always** include logs! + +### `Reproduction (Code)` + +> ⚠ī¸ Please remember that, with sample code; it's easier to reproduce a bug and much faster to fix it. + +> 🔗 Please refer to a simple code example. + +```bash +$ git clone https://github.com// +``` + +### `Environment` + +> ℹī¸ Please provide information about your current environment. + +|OS|node|npm/yarn|package| +|:-:|:--:|:-:|:------:| +|[name][version]|[version]|[version]|[version]| diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..3f7ed29 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,55 @@ +### `Notable Changes` + +> ✏ī¸ Describe the big picture of your changes here to communicate to the maintainers why we should accept this pull request. If it fixes a bug or resolves a feature request, be sure to link to that issue down below. + +#### `Commit Message Summary (CHANGELOG)` + +``` +commit message body... +``` + +### `Type` + +> ℹī¸ What types of changes does your code introduce? + +> 👉 _Put an `x` in the boxes that apply and delete all others_ + +- [ ] CI +- [ ] Fix +- [ ] Perf +- [ ] Docs +- [ ] Test +- [ ] Chore +- [ ] Style +- [ ] Build +- [ ] Feature +- [ ] Refactor + +### `SemVer` + +> ℹī¸ What changes to the current `semver` range does your PR introduce? + +> 👉 _Put an `x` in the boxes that apply and delete all others_ + +- [ ] Fix (:label: Patch) +- [ ] Feature (:label: Minor) +- [ ] Breaking Change (:label: Major) + +### `Issues` + +> ℹī¸ What issue(s) (if any) are closed by your PR? + +> 👉 _Replace `#1` with the issue number that applies and remove the ``` ` ```_ + +- Fixes `#1` + +### `Checklist` + +> ℹī¸ You can also fill these out after creating the PR. If you're unsure about any of them, don't hesitate to ask. This is a reminder of what we are going to look for before merging your code. + +> 👉 _Put an `x` in the boxes that apply and delete all others._ + +- [ ] Lint and unit tests pass with my changes +- [ ] I have added tests that prove my fix is effective/works +- [ ] I have added necessary documentation (if appropriate) +- [ ] Any dependent changes are merged and published in downstream modules diff --git a/.gitignore b/.gitignore index 0a1dee2..618d553 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,12 @@ # OS + .DS_Store ._* # NODEJS -node_modules + npm-debug.log +node_modules +.nyc_output coverage diff --git a/.jscsrc b/.jscsrc deleted file mode 100644 index f5cc4fc..0000000 --- a/.jscsrc +++ /dev/null @@ -1,61 +0,0 @@ -{ - "excludeFiles": [ - ".git/**", - "node_modules/**", - "coverage/**" - ], - "fileExtensions": [".js"], - "validateParameterSeparator": ", ", - "disallowSpacesInCallExpression": true, - "disallowSpaceAfterObjectKeys": true, - "disallowSpaceBeforeSemicolon": true, - "disallowSpaceBeforePostfixUnaryOperators": true, - "disallowNewlineBeforeBlockStatements": true, - "disallowMultipleLineBreaks": true, - "disallowTrailingComma": true, - "disallowTrailingWhitespace": true, - "disallowNamedUnassignedFunctions": true, - "disallowSpacesInFunctionDeclaration": { - "beforeOpeningRoundBrace": true - }, - "disallowSpacesInFunctionExpression": { - "beforeOpeningRoundBrace": true - }, - "disallowSpacesInConditionalExpression": { - "afterTest": true, - "afterConsequent": true - }, - "requireSpaceBeforeBlockStatements": 1, - "requireSpaceAfterKeywords": [ - "do", - "for", - "if", - "else", - "switch", - "case", - "try", - "catch", - "void", - "while", - "with", - "return", - "typeof" - ], - "requireSpaceAfterBinaryOperators": true, - "requireSpacesInForStatement": true, - "requireSpaceBetweenArguments": true, - "requireSemicolons": true, - "requireFunctionDeclarations": true, - "requireCommaBeforeLineBreak": true, - "requireSpacesInConditionalExpression": { - "afterTest": true, - "afterConsequent": true - }, - "requireSpacesInFunctionDeclaration": { - "beforeOpeningCurlyBrace": true - }, - "requireSpacesInConditionalExpression": { - "beforeConsequent": true, - "beforeAlternate": true - } -} diff --git a/.jshintignore b/.jshintignore deleted file mode 100644 index ba2a97b..0000000 --- a/.jshintignore +++ /dev/null @@ -1,2 +0,0 @@ -node_modules -coverage diff --git a/.jshintrc b/.jshintrc deleted file mode 100644 index dfd00eb..0000000 --- a/.jshintrc +++ /dev/null @@ -1,8 +0,0 @@ -{ - "eqeqeq": true, - "expr": true, - "maxlen": 120, - "undef": true, - "unused": true, - "node": true -} diff --git a/.npmignore b/.npmignore index 0b4cf45..50cc70e 100644 --- a/.npmignore +++ b/.npmignore @@ -1,7 +1,7 @@ -node_modules/ -test/ npm-debug.log .editorconfig -.jscsrc -.jshintignore -.jshintrc + +.nyc_output +test/ +coverage/ +node_modules/ diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..43c97e7 --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +package-lock=false diff --git a/.travis.yml b/.travis.yml index a6f07f4..82e5558 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,14 +1,11 @@ -sudo: false language: node_js -node_js: - - "4.1" - - "0.12" - - "0.10" -env: - global: - - ISTANBUL_COVERAGE: yes +node_js: + - "stable" + - "lts/*" + - 6 + - 4 after_success: - npm i coveralls - - cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js && echo "Coverage data was sent to coveralls!" + - cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js diff --git a/LICENSE b/LICENSE index 2feff84..a835316 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ -(The MIT License) +License (MIT) -Copyright (c) 2015 Ivan Voischev +Copyright (c) 2017 Ivan Voischev Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/MAINTAINERS b/MAINTAINERS deleted file mode 100644 index 992aa6a..0000000 --- a/MAINTAINERS +++ /dev/null @@ -1,5 +0,0 @@ -voischev -awinogradov -zxqfox -Yeti-or -jescalan diff --git a/PARSER.md b/PARSER.md new file mode 100644 index 0000000..9008fbd --- /dev/null +++ b/PARSER.md @@ -0,0 +1,13 @@ + + +## parser(html, options) ⇒ Array +Parse HTML to PostHTML Tree + +**Kind**: global function +**Returns**: Array - result + +| Param | Type | +| --- | --- | +| html | String | +| options | Object | + diff --git a/README.md b/README.md index 5b7da22..a096ab8 100644 --- a/README.md +++ b/README.md @@ -1,89 +1,151 @@ -# posthtml-parser -[![npm version](https://badge.fury.io/js/posthtml-parser.svg)](http://badge.fury.io/js/posthtml-parser) -[![Build Status](https://travis-ci.org/posthtml/posthtml-parser.svg?branch=master)](https://travis-ci.org/posthtml/posthtml-parser?branch=master) -[![Coverage Status](https://coveralls.io/repos/posthtml/posthtml-parser/badge.svg?branch=master)](https://coveralls.io/r/posthtml/posthtml-parser?branch=master) +[![npm][npm]][npm-url] +[![node][node]][node-url] +[![deps][deps]][deps-url] +[![tests][tests]][tests-url] +[![coverage][cover]][cover-url] +[![chat][chat]][chat-url] + +
+ + PostHTML + +

PostHTML Parser

+

Parses HTML to PostHTML Tree

+
+ +

Install

+ +```bash +npm i -D posthtml-parser +``` + +

Usage

-Parse HTML/XML to [PostHTML AST](https://github.com/posthtml/posthtml-parser#posthtml-ast-format). -More about [PostHTML](https://github.com/posthtml/posthtml#readme) +```js +const parser = require('posthtml-parser') -## Install +const options = {} -[NPM](http://npmjs.com) install +const tree = parser(options)(html) ``` -$ npm install posthtml-parser + +### `AST Format` + +Any parser being used with PostHTML should return a standard PostHTML [Abstract Syntax Tree](https://www.wikiwand.com/en/Abstract_syntax_tree) (AST). Fortunately, this is a very easy format to produce and understand. The AST is an array that can contain strings and objects. Any strings represent plain text content to be written to the output. Any objects represent HTML tags. + +Tag objects generally look something like this: + +#### `Tag {Object}` + +```js +{ + tag: 'div', + attrs: { + class: 'name' + }, + content: [ 'Hello World!' ] +} ``` -## Usage +#### `Text (Content) {String}` -#### Input HTML -```html - - Cat - +```js +'Hello World!' ``` + +Tag objects can contain three keys. The `tag` key takes the name of the tag as the value. This can include custom tags. The optional `attrs` key takes an object with key/value pairs representing the attributes of the html tag. A boolean attribute has an empty string as its value. Finally, the optional `content` key takes an array as its value, which is a PostHTML AST. In this manner, the AST is a tree that should be walked recursively. + +

Options

+ +|Name|Type|Default|Description| +|:--:|:--:|:-----:|:----------| +|**[`tags`](#tags)**|`{Array}`|`[ import, include ]`|Specify custom self closing tags| +|**[`directives`](#directives)**|`{Array}`|`[ doctype, ?php ]`|Specify custom directives| + +### `tags` + ```js const parser = require('posthtml-parser') -const fs = require('fs') -const html = fs.readFileSync('path/to/input.html').toString() -console.log(parser(html)) // Logs a PostHTML AST +const tags = [ 'import' ] + +const tree = parser({ tags })(html) ``` -#### input HTML +### `directives` + +```js +const parser = require('posthtml-parser') + +const directives = [ + { name: '?', start: '<', end: '>' } +] + +const tree = parser({ directives })(html) +``` + +

Examples

+ +**file.html** ```html Cat ``` -#### Result PostHTMLTree ```js -[{ +const parser = require('posthtml-parser') + +const html = ` + + Cat + +` +const options = {} + +const tree = parser(options)(html) +``` + +```js +[ + { tag: 'a', attrs: { - class: 'animals', - href: '#' + class: 'animals', + href: '#' }, content: [ - '\n ', - { - tag: 'span', - attrs: { - class: 'animals__cat', - style: 'background: url(cat.png)' - }, - content: ['Cat'] + '\n ', + { + tag: 'span', + attrs: { + class: 'animals__cat', + style: 'background: url(cat.png)' }, - '\n' + content: [ 'Cat' ] + }, + '\n' ] -}] + } +] ``` -## PostHTML AST Format -Any parser being used with PostHTML should return a standard PostHTML [Abstract Syntax Tree](https://www.wikiwand.com/en/Abstract_syntax_tree) (AST). Fortunately, this is a very easy format to produce and understand. The AST is an array that can contain strings and objects. Any strings represent plain text content to be written to the output. Any objects represent HTML tags. +[npm]: https://img.shields.io/npm/v/posthtml-parser.svg +[npm-url]: https://npmjs.com/package/posthtml-parser -Tag objects generally look something like this: +[node]: https://img.shields.io/node/v/posthtml-parser.svg +[node-url]: https://nodejs.org -```js -{ - tag: 'div', - attrs: { - class: 'foo' - }, - content: ['hello world!'] -} -``` - -Tag objects can contain three keys. The `tag` key takes the name of the tag as the value. This can include custom tags. The optional `attrs` key takes an object with key/value pairs representing the attributes of the html tag. A boolean attribute has an empty string as its value. Finally, the optional `content` key takes an array as its value, which is a PostHTML AST. In this manner, the AST is a tree that should be walked recursively. - -## Options +[deps]: https://david-dm.org/posthtml/posthtml-parser.svg +[deps-url]: https://david-dm.org/posthtml/posthtml-parser -### `directives` -Type: `Array` -Default: `[{name: '!doctype', start: '<', end: '>'}]` -Description: *Adds processing of custom directives* +[tests]: http://img.shields.io/travis/posthtml/posthtml-parser.svg +[tests-url]: https://travis-ci.org/posthtml/posthtml-parser -## License +[cover]: https://coveralls.io/repos/github/posthtml/posthtml-parser/badge.svg +[cover-url]: https://coveralls.io/github/posthtml/posthtml-parser -[MIT](LICENSE) +[chat]: https://badges.gitter.im/posthtml/posthtml.svg +[chat-url]: https://gitter.im/posthtml/posthtml diff --git a/index.js b/index.js deleted file mode 100644 index da6c828..0000000 --- a/index.js +++ /dev/null @@ -1,122 +0,0 @@ -'use strict'; - -var htmlparser = require('htmlparser2'); -var isObject = require('isobject'); - -/** - * @see https://github.com/fb55/htmlparser2/wiki/Parser-options - */ -var defaultOptions = {lowerCaseTags: false, lowerCaseAttributeNames: false}; - -var defaultDirectives = [{name: '!doctype', start: '<', end: '>'}]; - -/** - * Parse html to PostHTMLTree - * @param {String} html - * @param {Object} [options=defaultOptions] - * @return {PostHTMLTree} - */ -function postHTMLParser(html, options) { - var bufArray = [], - results = []; - - bufArray.last = function() { - return this[this.length - 1]; - }; - - function parserDirective(name, data) { - var directives = options.directives || defaultDirectives; - var last = bufArray.last(); - - for (var i = 0; i < directives.length; i++) { - var directive = directives[i]; - var directiveText = directive.start + data + directive.end; - - if (name.toLowerCase() === directive.name) { - if (!last) { - results.push(directiveText); - return; - } - - last.content || (last.content = []); - last.content.push(directiveText); - } - } - } - - var parser = new htmlparser.Parser({ - onprocessinginstruction: parserDirective, - oncomment: function(data) { - var comment = '', - last = bufArray.last(); - - if (!last) { - results.push(comment); - return; - } - - last.content || (last.content = []); - last.content.push(comment); - }, - onopentag: function(tag, attrs) { - var buf = { tag: tag }; - - if (Object.keys(attrs).length) { - buf.attrs = attrs; - } - - bufArray.push(buf); - }, - onclosetag: function() { - var buf = bufArray.pop(); - - if (!bufArray.length) { - results.push(buf); - return; - } - - var last = bufArray.last(); - if (!Array.isArray(last.content)) { - last.content = []; - } - - last.content.push(buf); - }, - ontext: function(text) { - var last = bufArray.last(); - if (!last) { - results.push(text); - return; - } - - last.content || (last.content = []); - last.content.push(text); - } - }, options || defaultOptions); - - parser.write(html); - parser.end(); - - return results; -} - -function parserWrapper() { - var option; - - function parser(html) { - var opt = option || defaultOptions; - return postHTMLParser(html, opt); - } - - if (arguments.length === 1 && isObject(arguments[0])) { - option = arguments[0]; - return parser; - } - - option = arguments[1]; - return parser(arguments[0]); -} - -module.exports = parserWrapper; -module.exports.defaultOptions = defaultOptions; -module.exports.defaultDirectives = defaultDirectives; diff --git a/lib/index.js b/lib/index.js new file mode 100644 index 0000000..24133a4 --- /dev/null +++ b/lib/index.js @@ -0,0 +1,14 @@ +'use strict' + +const parser = require('./parser') + +module.exports = function (options) { + options = Object.assign( + { lowerCaseTags: false, lowerCaseAttributeNames: false }, + options + ) + + return function (html) { + return parser(html, options) + } +} diff --git a/lib/parser.js b/lib/parser.js new file mode 100644 index 0000000..8e0805b --- /dev/null +++ b/lib/parser.js @@ -0,0 +1,124 @@ +'use strict' + +const Parser = require('htmlparser2/lib/Parser') + +const DIRECTIVES = [ + { name: '!doctype', start: '<', end: '>' }, + { name: '?php', start: '<', end: '>' } +] +/** + * Parse HTML to PostHTML Tree + * + * @method parser + * + * @param {String} html + * @param {Object} options + * + * @return {Array} result + */ +function parser (html, options) { + const buffer = [] + + buffer.last = function () { + return this[this.length - 1] + } + + const result = [] + + const directives = DIRECTIVES.concat(options.directives || []) + + const parse = new Parser({ + onprocessinginstruction: function (name, data) { + let last = buffer.last() + + directives.forEach((directive) => { + const node = directive.start + data + directive.end + + if (name.toLowerCase() === directive.name) { + if (!last) { + result.push(node) + + return + } + + last.content || (last.content = []) + last.content.push(node) + } + }) + }, + + oncomment: function (data) { + const comment = '' + + const last = buffer.last() + + if (!last) { + result.push(comment) + + return + } + + last.content || (last.content = []) + last.content.push(comment) + }, + + onopentag: function (tag, attrs) { + const node = {} + + node.tag = tag + + if (attrs && Object.keys(attrs).length > 0) { + node.attrs = {} + + Object.keys(attrs).forEach((attr) => { + node.attrs[attr] = attrs[attr] + }) + } + + buffer.push(node) + }, + + onclosetag: function () { + const node = buffer.pop() + + if (!buffer.length) { + // undefined for selfClosing tags + // since we don't have any contents + // or an additional closing tag + if (node) { + result.push(node) + } + + return + } + + const last = buffer.last() + + if (!Array.isArray(last.content)) { + last.content = [] + } + + last.content.push(node) + }, + + ontext: function (text) { + const last = buffer.last() + + if (!last) { + result.push(text) + + return + } + + last.content || (last.content = []) + last.content.push(text) + } + }, options) + + parse.write(html) + parse.end() + + return result +} + +module.exports = parser diff --git a/package.json b/package.json index 91a4aca..bea5849 100644 --- a/package.json +++ b/package.json @@ -2,40 +2,40 @@ "name": "posthtml-parser", "version": "0.3.0", "description": "Parse HTML/XML to PostHTMLTree", - "keywords": [ - "html", - "xml", - "parser", - "posthtml", - "posthtmltree" - ], - "main": "index.js", - "scripts": { - "test": "npm run lint && npm run coverage", - "lint": "jshint . && jscs -v .", - "coverage": "istanbul cover --report text --report html --report lcov node_modules/mocha/bin/_mocha", - "preversion": "npm test", - "postversion": "git push && git push --tags && rm -rf coverage" - }, - "repository": "posthtml/posthtml-parser", "author": "Ivan Voischev ", "license": "MIT", - "bugs": { - "url": "https://github.com/posthtml/posthtml-parser/issues" + "engines": { + "node": ">= 4" }, - "homepage": "https://github.com/posthtml/posthtml-parser#readme", + "main": "lib/index.js", + "files": [ + "lib" + ], "dependencies": { - "htmlparser2": "^3.8.3", - "isobject": "^2.1.0" + "htmlparser2": "^3.0.0" }, "devDependencies": { - "chai": "^3.3.0", - "istanbul": "^0.4.0", - "jscs": "^2.3.5", - "jshint": "^2.8.0", - "mocha": "^2.3.3", - "rewire": "^2.5.2", - "sinon": "^1.17.4", - "sinon-chai": "^2.8.0" - } + "chai": "^4.0.0", + "jsdoc-to-markdown": "^3.0.0", + "mocha": "^4.0.0", + "nyc": "^11.0.0", + "standard": "^10.0.0", + "standard-version": "^4.0.0" + }, + "scripts": { + "lint": "standard --env mocha", + "test": "nyc node_modules/mocha/bin/_mocha", + "docs": "jsdoc2md lib/*.js > PARSER.md", + "clean": "rm -rf coverage .nyc_output", + "release": "standard-version" + }, + "keywords": [ + "html", + "xml", + "parser", + "posthtml" + ], + "repository": "https://github.com/posthtml/posthtml-parser", + "bugs": "https://github.com/posthtml/posthtml-parser/issues", + "homepage": "https://github.com/posthtml/posthtml-parser#readme" } diff --git a/test/parser.test.js b/test/parser.test.js new file mode 100644 index 0000000..293cee0 --- /dev/null +++ b/test/parser.test.js @@ -0,0 +1,235 @@ +'use strict' + +const it = require('mocha').it +const expect = require('chai').expect +const describe = require('mocha').describe + +const parser = require('../lib') + +describe('PostHTML Parser', function () { + describe('Doctypes', function () { + it('Doctype (uppercase)', function () { + const options = {} + + const fixture = '' + const expected = [ '' ] + + expect(parser(options)(fixture)).to.eql(expected) + }) + + it('Doctype', function () { + const options = {} + + const fixture = '' + const expected = [ '' ] + + expect(parser(options)(fixture)).to.eql(expected) + }) + }) + + describe('Comments', function () { + it('Comment', function () { + const options = {} + + const fixture = '' + const expected = [ '' ] + + expect(parser(options)(fixture)).to.eql(expected) + }) + + it('Comment (Content)', function () { + const options = {} + + const fixture = '
' + const expected = [ + { + tag: 'div', + content: [ '' ] + } + ] + + expect(parser(options)(fixture)).to.eql(expected) + }) + }) + + describe('XML', function () { + it('CDATA', function () { + const options = { xmlMode: true } + + const fixture = '' + const expected = [ + { + tag: 'script', + content: [ 'console.log(1);' ] + } + ] + + expect(parser(options)(fixture)).to.eql(expected) + }) + }) + + describe('Directives', function () { + it('Directive', function () { + const options = {} + + const fixture = '' + const expected = [ '' ] + + expect(parser(options)(fixture)).to.eql(expected) + }) + + it('Directive (Content)', function () { + const options = {} + + const fixture = '' + const expected = [ + '', + { + tag: 'html', + content: [ '' ] + } + ] + + expect(parser(options)(fixture)).to.eql(expected) + }) + + it('Directive (Custom)', function () { + const options = { + directives: [ + { name: '?=', start: '<', end: '>' } + ] + } + + const fixture = '' + const expected = [ '' ] + + expect(parser(options)(fixture)).to.eql(expected) + }) + }) + + describe('Tags', function () { + it('Doctype', function () { + const options = {} + + const fixture = '' + const expected = [ + '', + { tag: 'html' } + ] + + expect(parser(options)(fixture)).to.eql(expected) + }) + + it('Attrs', function () { + const options = {} + + const fixture = '
' + const expected = [ + { + tag: 'div', + attrs: { id: 'id', class: 'class' } + } + ] + + expect(parser(options)(fixture)).to.eql(expected) + }) + + it('Attrs', function () { + const options = {} + + const fixture = '
' + const expected = [ + { + tag: 'div', + attrs: { id: 'id', class: 'class' } + } + ] + + expect(parser(options)(fixture)).to.eql(expected) + }) + + it('selfClosing', function () { + const options = {} + + const fixture = '' + const expected = [ + { + tag: 'import', + attrs: { + src: 'path/to/file.html' + } + } + ] + + expect(parser(options)(fixture)).to.eql(expected) + }) + }) + + describe('Content', function () { + it('Text', function () { + const options = {} + + const fixture = 'Text' + const expected = [ 'Text' ] + + expect(parser(options)(fixture)).to.eql(expected) + }) + + it('Text (Content)', function () { + const options = {} + + const fixture = '
Text
' + const expected = [ + { + tag: 'div', + content: [ 'Text' ] + } + ] + + expect(parser(options)(fixture)).to.eql(expected) + }) + }) + + it('Integration - Tree', function () { + const options = {} + + const fixture = 'Text 1Text 2Text 3' + const expected = [ + { + tag: 'span', + content: [ 'Text 1' ] + }, + { + tag: 'span', + content: [ 'Text 2' ] + }, + 'Text 3' + ] + + expect(parser(options)(fixture)).to.eql(expected) + }) + + it('Integration - Node', function () { + const options = {} + + const fixture = '
Text 1Text 2Text 3
' + const expected = [ + { + tag: 'div', + content: [ + { + tag: 'span', + content: [ 'Text 1' ] + }, + { + tag: 'span', + content: [ 'Text 2' ] + }, + 'Text 3' + ] + } + ] + + expect(parser(options)(fixture)).to.eql(expected) + }) +}) diff --git a/test/test.js b/test/test.js deleted file mode 100644 index ed5696e..0000000 --- a/test/test.js +++ /dev/null @@ -1,134 +0,0 @@ -var parser = require('..'); -var parserWithMockedDeps = require('rewire')('..'); -var describe = require('mocha').describe; -var it = require('mocha').it; -var beforeEach = require('mocha').beforeEach; -var chai = require('chai'); -var sinon = require('sinon'); -var expect = chai.expect; -chai.use(require('sinon-chai')); - -describe('PostHTML-Parser test', function() { - describe('Call signatures', function() { - var customOptions = {lowerCaseTags: false, lowerCaseAttributeNames: false}; - var MockedHtmlParser2; - var parserSpy; - - beforeEach(function() { - // jscs:disable requireFunctionDeclarations - MockedHtmlParser2 = function() {}; - MockedHtmlParser2.prototype = { - write: function() {}, - end: function() {} - }; - // jscs:enable requireFunctionDeclarations - - // Create spy on mocked htmlparser2 to collect call stats - parserSpy = sinon.spy(MockedHtmlParser2); - - // Replace real htmlparser2 dependency of posthtml-parser with mocked - parserWithMockedDeps.__set__({ - htmlparser: {Parser: parserSpy} - }); - }); - - it('should use default options when called with 1 param', function() { - parserWithMockedDeps(''); - expect(parserSpy.firstCall.args[1]).to.eql(parser.defaultOptions); - }); - - it('should use custom options when called with 2 params', function() { - parserWithMockedDeps('', customOptions); - expect(parserSpy.firstCall.args[1]).to.eql(customOptions); - }); - - it('should use custom params when called as factory function', function() { - var factory = parserWithMockedDeps(customOptions); - expect(factory).to.be.a('function'); - expect(factory('')).to.be.an('array'); - expect(parserSpy.firstCall.args[1]).to.eql(customOptions); - }); - }); - - it('should be parse doctype in uppercase', function() { - expect(parser('')).to.eql(['']); - }); - - it('should be parse comment', function() { - expect(parser('')).to.eql(['']); - }); - - it('should be parse comment in content', function() { - expect(parser('
')).to.eql([{tag: 'div', content: ['']}]); - }); - - it('should be parse doctype', function() { - expect(parser('')).to.eql(['']); - }); - - it('should be parse directive', function() { - var customDirectives = {directives: [ - {name: '?php', start: '<', end: '>'} - ]}; - - expect(parser('', customDirectives)).to.eql(['']); - }); - - it('should be parse directives and tag', function() { - var customDirectives = {directives: [ - {name: '!doctype', start: '<', end: '>'}, - {name: '?php', start: '<', end: '>'} - ]}; - - var html = ''; - var tree = [ - '', - { - content: [''], - tag: 'html' - } - ]; - - expect(parser(html, customDirectives)).to.eql(tree); - }); - - it('should be parse tag', function() { - expect(parser('')).to.eql([{ tag: 'html' }]); - }); - - it('should be parse doctype and tag', function() { - expect(parser('')).to.eql(['', { tag: 'html' }]); - }); - - it('should be parse tag attrs', function() { - expect(parser('
')).to.eql([{ - tag: 'div', attrs: { id: 'id', class: 'class'} } - ]); - }); - - it('should be parse text', function() { - expect(parser('Text')).to.eql(['Text']); - }); - - it('should be parse text in content', function() { - expect(parser('
Text
')).to.eql([{ tag: 'div', content: ['Text'] }]); - }); - - it('should be parse not a single node in tree', function() { - expect(parser('Text1Text2Text3')).to.eql([ - { tag: 'span', content: ['Text1']}, { tag: 'span', content: ['Text2']}, 'Text3' - ]); - }); - - it('should be parse not a single node in parent content', function() { - expect(parser('
Text1Text2Text3
')).to.eql([ - { tag: 'div', content: [{ tag: 'span', content: ['Text1']}, { tag: 'span', content: ['Text2']}, 'Text3'] } - ]); - }); - - it('should be parse camelCase tag name', function() { - expect(parser('')).to.eql([ - { tag: 'mySuperTag' } - ]); - }); -});