Skip to content

Commit

Permalink
Split up markdown-js into multiple files, build system assembles from…
Browse files Browse the repository at this point in the history
… them for web use.
  • Loading branch information
ashb committed Sep 15, 2013
2 parents 95f7b8e + 5d6efbc commit 558d7c9
Show file tree
Hide file tree
Showing 22 changed files with 2,082 additions and 1,709 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@
.DS_Store
/node_modules/
/npm-debug.log
dist
lib/markdown.js
2 changes: 2 additions & 0 deletions .npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
test
src

This comment has been minimized.

Copy link
@XhmikosR

XhmikosR Sep 15, 2013

Contributor

Hmm this file shouldn't be needed...

"files": [

5 changes: 4 additions & 1 deletion Changes.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@

- Fix content tight between two hr's disappearing (#106)
- Fix (yet more! gah) global variable leaks #99
- Fix JSHint warnings #65 - Thanks XhmikosR!
- Use JSHint ot validate code style and fix numerous warnings it flagged up
(#65) Thanks XhmikosR!
- Use grunt to build tailored versions including allowing customizing of what
dialects are included (#113 - Robin Ward)

## v0.5.0 - 2013-07-26

Expand Down
35 changes: 33 additions & 2 deletions Gruntfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ module.exports = function(grunt) {
},

jshint: {
files: ['Gruntfile.js', 'lib/**/*.js', 'test/**/*.js'],
files: ['Gruntfile.js', 'src/**/*.js', 'test/**/*.js'],
options: {
"browser": false,
"maxerr": 100,
Expand All @@ -30,11 +30,13 @@ module.exports = function(grunt) {
"eqnull": true,
"forin": false,
"globals": {
"define": true,
"print": true,
"uneval": true,
"window": true
},
"immed": true,
"indent": 2,
"latedef": true,
"laxbreak": true,
"laxcomma": true,
Expand All @@ -53,11 +55,40 @@ module.exports = function(grunt) {
"unused": false,
ignores: ['.git', 'node_modules']
}
},

build: {
web: {
dest: "dist/markdown.js",
minimum: ["parser"],
removeWith: ['dialects/gruber'],
startFile: "inc/header.js",
endFile: "inc/footer-web.js"
},
node: {
dest: "lib/markdown.js",
minimum: ["parser"],
removeWith: ['dialects/gruber'],
startFile: "inc/header.js",
endFile: "inc/footer-node.js"
}
},

uglify: {
my_target: {
files: {
'dist/markdown.min.js': ['dist/markdown.js']
}
}
}

});

grunt.registerTask('default', ['test']);
grunt.registerTask('all', ['test', 'build', 'uglify']);
grunt.registerTask('default', ['all']);
grunt.registerTask('test', 'Runs all tests and linting', ['node_tap', 'jshint']);
grunt.loadNpmTasks('grunt-node-tap');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadTasks("inc/tasks");
};
30 changes: 30 additions & 0 deletions README.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,13 @@ Optionally, install `md2html` into your path

npm install -g markdown

### In the browser

If you want to use from the browser go to the [releases page on GitHub] and
download the version you want (minified or not).

[releases]: https://github.com/evilstreak/markdown-js/releases

## Usage

### Node
Expand Down Expand Up @@ -155,6 +162,29 @@ For example, you may want to grab a list of all URLs linked to in the
document before rendering it to HTML which you could do by recursing
through the HTML tree looking for `a` nodes.

## Building and Testing markdown-js

We use [Grunt](http://gruntjs.com/) to build and run markdown-js's tests.
Make sure you run `npm install` to install the developer dependencies for
the project, then you can:

$ npm test

To run our test suite. If you'd like to build markdown-js, you can run:

$ ./node_modules/.bin/grunt all

This command will run all the tests, then output a concatenated markdown.js
and markdown.min.js in the `dist/` directory for use in a browser application.

## Building a custom markdown-js

By default, you will get the Gruber and Maruku dialects included when you run
`grunt all`. However, you can create a custom build using the following syntax
if you don't want to include Maruku support.

$ ./node_modules/.bin/grunt "custom:-dialects/maruku"

## Running tests

To run the tests under node you will need tap installed (it's listed as a
Expand Down
8 changes: 8 additions & 0 deletions inc/footer-node.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@

expose.Markdown = Markdown;
expose.parse = Markdown.parse;
expose.toHTML = Markdown.toHTML;
expose.toHTMLTree = Markdown.toHTMLTree;
expose.renderJsonML = Markdown.renderJsonML;

})(exports);
11 changes: 11 additions & 0 deletions inc/footer-web.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@

expose.Markdown = Markdown;
expose.parse = Markdown.parse;
expose.toHTML = Markdown.toHTML;
expose.toHTMLTree = Markdown.toHTMLTree;
expose.renderJsonML = Markdown.renderJsonML;

})(function() {
window.markdown = {};
return window.markdown;
}());
7 changes: 7 additions & 0 deletions inc/header.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// Released under MIT license
// Copyright (c) 2009-2010 Dominic Baggott
// Copyright (c) 2009-2010 Ash Berlin
// Copyright (c) 2011 Christoph Dorn <[email protected]> (http://www.christophdorn.com)
// Date: @DATE

(function(expose) {
224 changes: 224 additions & 0 deletions inc/tasks/build_markdown.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,224 @@
/**
Tasks cribbed from jQuery to handle building custom markdown parsers.
*/

module.exports = function( grunt ) {

"use strict";

var fs = require( "fs" ),
srcFolder = __dirname + "/../../src/",
rdefineEnd = /\}\);[^}\w]*$/,
// This is temporary until the skipSemiColonInsertion option makes it to NPM
requirejs = require( "requirejs" ),
config = {
baseUrl: "src",
name: "markdown",
// We have multiple minify steps
optimize: "none",
skipSemiColonInsertion: true,
onBuildWrite: convert
};

/**
* Strip all definitions generated by requirejs
* Convert "var" modules to var declarations
* "var module" means the module only contains a return statement that should be converted to a var declaration
* This is indicated by including the file in any "var" folder
* @param {String} name
* @param {String} path
* @param {String} contents The contents to be written (including their AMD wrappers)
*/
function convert( name, path, contents ) {
// Convert var modules
if ( /.\/var\//.test( path ) ) {
contents = contents
.replace( /define\([\w\W]*?return/, "var " + (/var\/([\w-]+)/.exec(name)[1]) + " =" )
.replace( rdefineEnd, "" );

} else {

contents = contents
.replace( /\s*return\s+[^\}]+(\}\);[^\w\}]*)$/, "$1" );

// Remove define wrappers, closure ends, and empty declarations
contents = contents
.replace( /define\([^{]*?{/, "" )
.replace( rdefineEnd, "" );

// Remove empty definitions
contents = contents
.replace( /define\(\[[^\]]+\]\)[\W\n]+$/, "" );
}
return contents;
}

grunt.registerMultiTask(
"build",
"Concatenate source, remove sub AMD definitions, (include/exclude modules with +/- flags), embed date/version",
function() {
var flag, index,
done = this.async(),
flags = this.flags,
name = this.data.dest,
minimum = this.data.minimum,
removeWith = this.data.removeWith,
excluded = [],
included = [],
version = grunt.config( "pkg.version" ),
/**
* Recursively calls the excluder to remove on all modules in the list
* @param {Array} list
* @param {String} [prepend] Prepend this to the module name. Indicates we're walking a directory
*/
excludeList = function( list, prepend ) {
if ( list ) {
prepend = prepend ? prepend + "/" : "";
list.forEach(function( module ) {
// Exclude var modules as well
if ( module === "var" ) {
excludeList( fs.readdirSync( srcFolder + prepend + module ), prepend + module );
return;
}
if ( prepend ) {
// Skip if this is not a js file and we're walking files in a dir
if ( !(module = /([\w-\/]+)\.js$/.exec( module )) ) {
return;
}
// Prepend folder name if passed
// Remove .js extension
module = prepend + module[1];
}

// Avoid infinite recursion
if ( excluded.indexOf( module ) === -1 ) {
excluder( "-" + module );
}
});
}
},
/**
* Adds the specified module to the excluded or included list, depending on the flag
* @param {String} flag A module path relative to the src directory starting with + or - to indicate whether it should included or excluded
*/
excluder = function( flag ) {
var m = /^(\+|\-|)([\w\/-]+)$/.exec( flag ),
exclude = m[ 1 ] === "-",
module = m[ 2 ];

if ( exclude ) {
// Can't exclude certain modules
if ( minimum.indexOf( module ) === -1 ) {
// Add to excluded
if ( excluded.indexOf( module ) === -1 ) {
grunt.log.writeln( flag );
excluded.push( module );
// Exclude all files in the folder of the same name
// These are the removable dependencies
// It's fine if the directory is not there
try {
excludeList( fs.readdirSync( srcFolder + module ), module );
} catch( e ) {
grunt.verbose.writeln( e );
}
}
// Check removeWith list
excludeList( removeWith[ module ] );
} else {
grunt.log.error( "Module \"" + module + "\" is a mimimum requirement.");
if ( module === "selector" ) {
grunt.log.error( "If you meant to replace Sizzle, use -sizzle instead." );
}
}
} else {
grunt.log.writeln( flag );
included.push( module );
}
};

// append commit id to version
if ( process.env.COMMIT ) {
version += " " + process.env.COMMIT;
}

// figure out which files to exclude based on these rules in this order:
// dependency explicit exclude
// > explicit exclude
// > explicit include
// > dependency implicit exclude
// > implicit exclude
// examples:
// * none (implicit exclude)
// *:* all (implicit include)
// *:*:-css all except css and dependents (explicit > implicit)
// *:*:-css:+effects same (excludes effects because explicit include is trumped by explicit exclude of dependency)
// *:+effects none except effects and its dependencies (explicit include trumps implicit exclude of dependency)
for ( flag in flags ) {
if ( flag !== "*" ) {
excluder( flag );
}
}
grunt.verbose.writeflags( excluded, "Excluded" );
grunt.verbose.writeflags( included, "Included" );

// append excluded modules to version
if ( excluded.length ) {
version += " -" + excluded.join( ",-" );
// set pkg.version to version with excludes, so minified file picks it up
grunt.config.set( "pkg.version", version );
grunt.verbose.writeln( "Version changed to " + version );
// Have to use shallow or core will get excluded since it is a dependency
config.excludeShallow = excluded;
}
config.include = included;

config.wrap = {
startFile: this.data.startFile,
endFile: this.data.endFile
};


/**
* Handle Final output from the optimizer
* @param {String} compiled
*/
config.out = function( compiled ) {
compiled = compiled
// Embed Version
.replace( /@VERSION/g, version )
// Embed Date
// yyyy-mm-ddThh:mmZ
.replace( /@DATE/g, ( new Date() ).toISOString().replace( /:\d+\.\d+Z$/, "Z" ) );

// Write concatenated source to file
grunt.file.write( name, compiled );
};

// Trace dependencies and concatenate files
requirejs.optimize( config, function( response ) {
grunt.verbose.writeln( response );
grunt.log.ok( "File '" + name + "' created." );
done();
}, function( err ) {
done( err );
});
});

// Special "alias" task to make custom build creation less grawlix-y
// Translation example
//
// grunt custom:+ajax,-dimensions,-effects,-offset
//
// Becomes:
//
// grunt build:*:*:+ajax:-dimensions:-effects:-offset
grunt.registerTask( "custom", function() {
var args = [].slice.call( arguments ),
modules = args.length ? args[ 0 ].replace( /,/g, ":" ) : "";

grunt.log.writeln( "Creating custom build...\n" );

grunt.task.run([ "build:*:*:" + modules, 'uglify']);
});
};
Loading

0 comments on commit 558d7c9

Please sign in to comment.