diff --git a/.eslintrc.json b/.eslintrc.json
new file mode 100644
index 0000000..d9923b3
--- /dev/null
+++ b/.eslintrc.json
@@ -0,0 +1,16 @@
+{
+ "root": true,
+
+ "extends": "jquery",
+
+ "reportUnusedDisableDirectives": true,
+
+ "env": {
+ "es2023": true,
+ "node": true
+ },
+
+ "rules": {
+ "strict": [ "error", "global" ]
+ }
+}
diff --git a/.github/workflows/node.yml b/.github/workflows/node.yml
new file mode 100644
index 0000000..e39ad91
--- /dev/null
+++ b/.github/workflows/node.yml
@@ -0,0 +1,28 @@
+# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-nodejs
+
+name: Node.js
+
+on:
+ - push
+ - pull_request
+
+jobs:
+ build:
+
+ runs-on: ubuntu-latest
+
+ strategy:
+ matrix:
+ node-version: [18.x, 20.x]
+
+ steps:
+ - name: Install Debian packages
+ run: sudo apt-get install -y libxml2-utils xsltproc
+ - uses: actions/checkout@v3
+ - name: Install Node.js ${{ matrix.node-version }}
+ uses: actions/setup-node@v3
+ with:
+ node-version: ${{ matrix.node-version }}
+ cache: 'npm'
+ - run: npm ci
+ - run: npm test
diff --git a/.gitignore b/.gitignore
index b512c09..f484804 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,2 @@
-node_modules
\ No newline at end of file
+/node_modules
+/test/dist
diff --git a/.jshintrc b/.jshintrc
deleted file mode 100644
index d34c42d..0000000
--- a/.jshintrc
+++ /dev/null
@@ -1,17 +0,0 @@
-{
- "boss": true,
- "curly": true,
- "eqeqeq": true,
- "eqnull": true,
- "expr": true,
- "immed": true,
- "noarg": true,
- "onevar": true,
- "quotmark": "double",
- "smarttabs": true,
- "trailing": true,
- "undef": true,
- "unused": true,
-
- "node": true
-}
diff --git a/.mailmap b/.mailmap
new file mode 100644
index 0000000..1f2f86f
--- /dev/null
+++ b/.mailmap
@@ -0,0 +1 @@
+Michał Gołębiowski-Owczarek
diff --git a/.npmignore b/.npmignore
new file mode 100644
index 0000000..b0a3eb8
--- /dev/null
+++ b/.npmignore
@@ -0,0 +1,3 @@
+/.*
+/fixture
+/test
diff --git a/Gruntfile.js b/Gruntfile.js
index 0e77a9a..d73b79d 100644
--- a/Gruntfile.js
+++ b/Gruntfile.js
@@ -1,20 +1,32 @@
-module.exports = function( grunt ) {
-
-grunt.loadNpmTasks( "grunt-contrib-jshint" );
+"use strict";
-grunt.initConfig({
- watch: {
- files: "",
- tasks: "default"
- },
- jshint: {
- options: {
- jshintrc: true
+module.exports = function( grunt ) {
+ grunt.initConfig( {
+ xmllint: {
+ all: [
+ "fixture/entries/**",
+ "entries2html.xsl"
+ ]
+ },
+ "build-posts": {
+ page: "fixture/pages/**"
+ },
+ "build-resources": {
+ all: "fixture/resources/**"
+ },
+ "build-xml-entries": {
+ all: "fixture/entries/**"
},
- files: [ "Gruntfile.js", "tasks/**/*.js" ]
- }
-});
+ wordpress: {
+ url: "example.org",
+ username: "admin",
+ password: "admin",
+ dir: "test/dist/wordpress"
+ }
+ } );
-grunt.registerTask( "default", "jshint" );
+ grunt.loadTasks( "tasks" );
+ grunt.registerTask( "lint", [ "xmllint" ] );
+ grunt.registerTask( "build", [ "build-posts", "build-resources", "build-xml-entries" ] );
};
diff --git a/LICENSE.txt b/LICENSE.txt
index af932fa..1f0face 100644
--- a/LICENSE.txt
+++ b/LICENSE.txt
@@ -1,8 +1,5 @@
-Copyright jQuery Foundation and other contributors, https://jquery.org/
-
-This software consists of voluntary contributions made by many
-individuals. For exact contribution history, see the revision history
-available at https://github.com/jquery/grunt-jquery-content
+Copyright jQuery Foundation and other contributors, https://github.com/jquery/grunt-jquery-content
+Copyright Scott González, http://scottgonzalez.com
The following license applies to all parts of this software except as
documented below:
diff --git a/README.md b/README.md
index 2742866..6ecd675 100644
--- a/README.md
+++ b/README.md
@@ -1,82 +1,112 @@
+[](https://qunitjs.com/)
+
# grunt-jquery-content
-A collection of tasks for building the jQuery web sites via Grunt.
+A collection of Grunt tasks for deploying jQuery documentation sites.
-This module builds on top of [grunt-wordpress](https://github.com/scottgonzalez/grunt-wordpress), which builds on top of [Gilded WordPress](https://github.com/scottgonzalez/gilded-wordpress). See the Gilded WordPress documentation for details on the [directory structure and file formats](https://github.com/scottgonzalez/gilded-wordpress#directory-structure).
+This module builds on top of [node-wordpress](https://github.com/scottgonzalez/node-wordpress) and the [Gilded WordPress](https://github.com/scottgonzalez/gilded-wordpress) plugin. See the Gilded WordPress documentation for details on the [directory structure and file formats](https://github.com/scottgonzalez/gilded-wordpress#directory-structure).
-## Tasks
+## Getting started
-### clean-dist
+Prerequisites:
-This task removes all files in the `dist/` directory.
+* Install the gilded-wordpress.php plugin on your WordPress site (copy from [Gilded WordPress](https://github.com/scottgonzalez/gilded-wordpress)).
+* Depending on what kind of files you want to upload as "resources", you may need to configure WordPress to allow more permissive uploads. See the [Gilded WordPress documentation](https://github.com/scottgonzalez/gilded-wordpress#permissive-uploads) for how to do this.
-### lint
+Basic set up for your project:
-This is an empty task list. If the site contains any lint checks, they should be defined here. For example, API sites should have the following task list:
+1. add `wordpress` configuration to Gruntfile.js.
+2. add `build-posts` task configuration to Gruntfile.js.
+3. add `grunt.registerTask( "build", [ "build-posts" ] );` to Gruntfile.js
-```
-grunt.registerTask( "lint", [ "xmllint" ] );
-```
+You can now use `grunt wordpress-deploy` to build and deploy your project.
-### build
+The `wordpress-deploy` task is a tree of the following tasks:
-This is a task list that must be defined per site, containing all of the build steps. A simple site would have the following task list:
+* `wordpress-deploy`
+ * `build-wordpress`
+ * `check-modules`
+ * `lint` (empty placeholder by default)
+ * `clean-dist`
+ * `build` (undefined by default)
+ * `wordpress-publish`
+ * `wordpress-validate`
+ * `wordpress-sync`
-```
-grunt.registerTask( "build", [ "build-posts", "build-resources" ] );
-```
+The following optional tasks are made available to use via the `lint` or `build` phase:
-### build-posts
+* lint:
+ * `xmllint`
+* build:
+ * `build-posts`
+ * `build-resources`
+ * `build-xml-entries`
+ * `build-xml-categories`
+ * `build-xml-full`
-This multi-task takes a list of html or markdown files, copies them to `[wordpress.dir]/posts/[post-type]/`, processes `@partial` entries and highlights the syntax in each. The keys are the post types for each set of posts.
+## Config
-See the [`postPreprocessors` export](#postpreprocessors) for a hook to implement custom processing.
+```javascript
+grunt.initConfig({
+ wordpress: {
+ url: "wordpress.localhost",
+ username: "admin",
+ password: "admin",
+ dir: "dist"
+ }
+});
+```
-#### markdown
+* `url`: The URL for the WordPress install.
+ Can be a full URL, e.g., `http://wordpress.localhost:1234/some/path`
+ or as short as just the host name.
+ If the protocol is `https`, then a secure connection will be used.
+* `host` (optional): The actual host to connect to if different from the URL, e.g., when deploying to a local server behind a firewall.
+* `username`: WordPress username.
+* `password`: WordPress password.
+* `dir`: Directory containing posts, taxonomies, and resources.
+ * See the [Gilded WordPress documentation](https://github.com/scottgonzalez/gilded-wordpress#directory-structure) for details on the directory structure and file formats.
-Using markdown files provides additional features over HTML files. By default, links for each header are automatically generated for markdown files.
+## Tasks
-In addition to the [standard metadata](https://github.com/scottgonzalez/gilded-wordpress#post-files) for post files, the following properties can be set:
+### clean-dist
-* `noHeadingLinks`: When set to `false`, heading links won't be generated.
-* `toc`: When set to `true`, a table of contents will be inserted at the top of the post based on the headings within the post.
+This task removes all files in the `dist/` directory.
-#### @partial
+### lint
-Usage:
+This is an empty task list by default. If the site contains any lint checks, they should be defined here. For example, API documentation sites should have the following task list:
-```html
-@partial(resources/code-sample.html)
+```javascript
+grunt.registerTask( "lint", [ "xmllint" ] );
```
-Where `resources/code-sample.html` is a relative path in the current directory. That html file will be inserted, escaped and highlighted.
-
-#### @placeholder
-
-Inside markup included with `@partial`, you can mark sections of code as `@placeholder` code, to be excluded from the inserted code, replaced with an html comment.
-
-Usage:
+### build-posts
-```html
-regular markup will show up here
-
-this will be replaced
-
-other content
+```javascript
+grunt.initConfig({
+ "build-posts": {
+ page: "pages/**"
+ },
+});
```
-That will result in:
+This multi-task takes a list of html or markdown files, copies them to `[wordpress.dir]/posts/[post-type]/`, processes `@partial` entries and highlights the syntax in each. The keys are the post types for each set of posts.
-```html
-regular markup will show up here
-
-other content
-```
+See the [`postPreprocessors` export](#postpreprocessors) for a hook to implement custom processing.
### build-resources
This mult-task copies all source files into `[wordpress.dir]/resources/`.
+```javascript
+grunt.initConfig({
+ "build-resources": {
+ all: "resources/**"
+ },
+});
+```
+
### xmllint
This multi-task lints XML files to ensure the files are valid.
@@ -112,19 +142,101 @@ Code examples in the descriptions will be syntax highlighted.
This task generates a single XML file that contains all entries and stores the result in `[wordpress.dir]/resources/api.xml`.
+### wordpress-validate
+
+Walks through the `wordpress.dir` directory and performs various validations, such as:
+
+* Verifying that XML-RPC is enabled for the WordPress site.
+* Verifying that the custom XML-RPC methods for gilded-wordpress are installed.
+* Verifying the taxonomies and terms in `taxonomies.json`.
+* Verifying that child-parent relationships for posts are valid.
+* Verifying data for each post.
+### wordpress-sync
+
+Synchronizes everything in `wordpress.dir` to the WordPress site.
+This will create/edit/delete terms, posts, and resources.
+
+*Note: `wordpress-validate` must run prior to `wordpress-sync`.*
+
+### wordpress-publish
+
+Alias task for `wordpress-validate` and `wordpress-sync`.
+This is useful if your original source content is already in the proper format,
+or if you want to manually verify generated content between your custom build and publishing.
+
+### wordpress-deploy
+
+Alias task for `build-wordpress` and `wordpress-publish`.
+This is useful if you are generating content for use with `wordpress-sync`.
+Simply create a `build-wordpress` task that populates the `wordpress.dir` directory
+and your deployments will be as simple as `grunt wordpress-deploy`.
+
+### deploy
+
+Alias task for `wordpress-deploy`.
+
+Since most projects that use grunt-jquery-content have one deploy target (WordPress),
+there is a built-in `deploy` task that just runs `wordpress-deploy`.
+
+If your project has other deploy targets, you can redefine `deploy` as an alias that runs both `wordpress-deploy` and your other deployment-related tasks.
+
+## Page content
+
+The following features are available in pages built via the `build-posts` task.
+
+### Markdown
+
+Using markdown files provides additional features over HTML files. By default, links for each header are automatically generated for markdown files.
+
+In addition to the [standard metadata](https://github.com/scottgonzalez/gilded-wordpress#post-files) for post files, the following properties can be set:
+
+* `noHeadingLinks`: When set to `false`, heading links won't be generated.
+* `toc`: When set to `true`, a table of contents will be inserted at the top of the post based on the headings within the post.
+
+### `@partial`
+
+Usage:
+
+```html
+@partial(resources/code-sample.html)
+```
+
+Where `resources/code-sample.html` is a relative path in the current directory. That html file will be inserted, escaped and highlighted.
+
+### `@placeholder`
+
+Inside markup included with `@partial`, you can mark sections of code as `@placeholder` code, to be excluded from the inserted code, replaced with an html comment.
+
+Usage:
+
+```html
+regular markup will show up here
+
+this will be replaced
+
+other content
+```
+
+That will result in:
+
+```html
+regular markup will show up here
+
+other content
+```
## Exports
-This module also exports some methods through the standard node `require()` API.
+The grunt-jquery-content module primarily registers Grunt tasks, but it also exports some methods through the `require()` API.
-### syntaxHighlight( content )
+### `syntaxHighlight( content )`
Syntax highlights content.
* `content` String: The string the highlight.
-### postPreprocessors
+### `postPreprocessors`
Hooks for modifying the posts before they're processed in the [`build-posts`](#build-posts) task.
diff --git a/entries2html.xsl b/entries2html.xsl
new file mode 100644
index 0000000..aed39b9
--- /dev/null
+++ b/entries2html.xsl
@@ -0,0 +1,11 @@
+
+
+
+
+
+<!doctype html>
+<style> </style>
+
+
+
+
diff --git a/fixture/entries/addClass.xml b/fixture/entries/addClass.xml
new file mode 100644
index 0000000..6d0f2b4
--- /dev/null
+++ b/fixture/entries/addClass.xml
@@ -0,0 +1,50 @@
+
+
+ .addClass()
+
+ 1.0
+
+ One or more space-separated classes to be added.
+
+
+
+ 1.4
+
+ A function returning one or more space-separated class names.
+
+
+
+
+
+ Adds the specified class(es) to each element in the set of matched elements.
+
+ This method does not replace a class. It simply adds the class.
+
+$( "p" ).addClass( "myClass yourClass" );
+
+ The .addClass()
method changes the className
property on the selected elements.
+
+
+ Add the class "selected" to the matched elements.
+
+
+
+
+
+
+
diff --git a/fixture/pages/Hello_World.html b/fixture/pages/Hello_World.html
new file mode 100644
index 0000000..4d91798
--- /dev/null
+++ b/fixture/pages/Hello_World.html
@@ -0,0 +1,17 @@
+
+Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque pellentesque placerat arcu, vel viverra augue posuere commodo. Aenean hendrerit quam sed commodo pellentesque.
+Donec sed commodo velit, non molestie justo.
+Phasellus
+Cras vel justo molestie lorem auctor convallis. Donec ac lacus tincidunt, euismod lectus eu, pulvinar tortor.
+ Hello.world({
+ // Handle the event
+ // ......
+ });
+
+Sed sed molestie purus
+Aliquam venenatis sem elit, et aliquet libero ultrices vitae. Nullam rutrum convallis justo, sed suscipit leo facilisis et.
+
diff --git a/fixture/pages/Mark.md b/fixture/pages/Mark.md
new file mode 100644
index 0000000..2eff544
--- /dev/null
+++ b/fixture/pages/Mark.md
@@ -0,0 +1,17 @@
+
+
+**Conubia linguae** hydrae novissima recepto certe, clarus quod amictus tum ignota fluctibus *et quod*, est verba capitum, variusque. Sui saevam gentes propiora Cycladas, Hecate stamina nurus ramum!
+
+## Oculi miserarum
+
+Lorem [markdownum](https://en.wikipedia.org/wiki/Markdown) silentia umerique, colla. Per felix innoxia pariterque capillos accessit, nec ad tempore in nubes detrahitur.
+
+Aures precantibus supplice Medusaeo, Lycormas est esse aestuat aut Pterelas.
+
+## Domitos interea
+
+1. Non torsi numine amor
+2. Tamen vino hinc indignatus aquas iunguntur sacrifica
+3. Solitum bacae tellure ille
diff --git a/fixture/resources/x.txt b/fixture/resources/x.txt
new file mode 100644
index 0000000..587be6b
--- /dev/null
+++ b/fixture/resources/x.txt
@@ -0,0 +1 @@
+x
diff --git a/index.js b/index.js
index d959c0f..08bea70 100644
--- a/index.js
+++ b/index.js
@@ -1,9 +1,11 @@
-var syntaxHighlight = require( "./lib/highlight" );
+"use strict";
+
+const syntaxHighlight = require( "./lib/highlight" );
exports.syntaxHighlight = syntaxHighlight;
exports.postPreprocessors = {
- _default: function( post, fileName, callback ) {
+ _default( post, _fileName, callback ) {
callback( null, post );
}
};
diff --git a/lib/highlight.js b/lib/highlight.js
index beedf71..96e5b00 100644
--- a/lib/highlight.js
+++ b/lib/highlight.js
@@ -1,20 +1,22 @@
-var fs = require( "fs" ),
- hljs = require( "highlight.js" ),
- cheerio = require( "cheerio" ),
- he = require( "he" ),
- grunt = require( "grunt" ),
- lineNumberTemplate = fs.readFileSync( __dirname + "/lineNumberTemplate.jst", "utf-8" );
+"use strict";
+
+const fs = require( "fs" );
+const hljs = require( "highlight.js" );
+const cheerio = require( "cheerio" );
+const he = require( "he" );
+const grunt = require( "grunt" );
+const lineNumberTemplate = fs.readFileSync( __dirname + "/lineNumberTemplate.jst", "utf-8" );
// When parsing the class attribute, make sure a class matches an actually
// highlightable language, instead of being presentational (e.g. 'example')
function getLanguageFromClass( str ) {
- var classes = (str || "").split( " " ),
+ var classes = ( str || "" ).split( " " ),
i = 0,
length = classes.length;
for ( ; i < length; i++ ) {
- if ( hljs.LANGUAGES[ classes[ i ].replace( /^lang-/, "" ) ] ) {
- return classes[i].replace( /^lang-/, "" );
+ if ( hljs.getLanguage( classes[ i ].replace( /^lang-/, "" ) ) ) {
+ return classes[ i ].replace( /^lang-/, "" );
}
}
@@ -27,61 +29,64 @@ function outdent( string ) {
minTabs = Infinity,
rLeadingTabs = /^\t+/;
- string.split( "\n" ).forEach(function( line, i, arr ) {
+ string.split( "\n" ).forEach( function( line, i, arr ) {
// Don't include first or last line if it's nothing but whitespace
- if ( (i === 0 || i === arr.length - 1) && !line.trim().length ) {
+ if ( ( i === 0 || i === arr.length - 1 ) && !line.trim().length ) {
return;
}
// For empty lines inside the snippet, push a space so the line renders properly
if ( !line.trim().length ) {
- adjustedLines.push(" ");
+ adjustedLines.push( " " );
return;
}
// Count how many leading tabs there are and update the global minimum
var match = line.match( rLeadingTabs ),
- tabs = match ? match[0].length : 0;
+ tabs = match ? match[ 0 ].length : 0;
minTabs = Math.min( minTabs, tabs );
adjustedLines.push( line );
- });
+ } );
if ( minTabs !== Infinity ) {
// Outdent the lines as much as possible
rOutdent = new RegExp( "^\t{" + minTabs + "}" );
- adjustedLines = adjustedLines.map(function( line ) {
+ adjustedLines = adjustedLines.map( function( line ) {
return line.replace( rOutdent, "" );
- });
+ } );
}
return adjustedLines.join( "\n" );
}
function syntaxHighlight( html ) {
- var $ = cheerio.load( html );
- $( "pre > code" ).each(function() {
+ // The third parameter is `false` to disable wrapping contents
+ // in `...`, etc.
+ var $ = cheerio.load( html, null, false );
+
+ $( "pre > code" ).each( function() {
var $t = $( this ),
code = he.decode( outdent( $t.html() ) ),
lang = $t.attr( "data-lang" ) ||
getLanguageFromClass( $t.attr( "class" ) ) ||
- (code.trim().charAt( 0 ) === "<" ? "xml" : "") ||
+ ( code.trim().charAt( 0 ) === "<" ? "xml" : "" ) ||
"javascript",
linenumAttr = $t.attr( "data-linenum" ),
- linenum = (linenumAttr === "true" ? 1 : parseInt( linenumAttr, 10 ) ) || 1,
- gutter = linenumAttr === undefined ? false : true,
- highlighted = hljs.highlight( lang, code ),
- fixed = hljs.fixMarkup( highlighted.value, " " );
+ linenum = parseInt( linenumAttr, 10 ) || 1,
+ gutter = linenumAttr !== "false",
+ highlighted = hljs.highlight( code, { language: lang } ),
+ fixed = highlighted.value.replace( /\t/g, " " );
// Handle multi-line comments (#32)
fixed = fixed.replace(
- /