mirror of
https://github.com/showdownjs/showdown.git
synced 2024-03-22 13:30:55 +08:00
feature(extensionLoading): add support to legacy extensions in the new extension mechanism
Old extensions that register themselves in `showdown.extensions` can be loaded and validated using the new extension loading mechanism. However, a warn is issued, alerting users and developers that the extension should be updated to use the new mechanism BREAKING CHANGE: Deprecates `showdown.extensions` property. To migrate, you should use the new method `showdown.extension(<ext name>, <extension>)` to register the extension.
This commit is contained in:
parent
e7cb15f1e9
commit
4ebd0caa27
|
@ -108,10 +108,11 @@ module.exports = function (grunt) {
|
||||||
|
|
||||||
require('load-grunt-tasks')(grunt);
|
require('load-grunt-tasks')(grunt);
|
||||||
|
|
||||||
|
grunt.registerTask('concatenate', ['concat']);
|
||||||
grunt.registerTask('lint', ['jshint', 'jscs']);
|
grunt.registerTask('lint', ['jshint', 'jscs']);
|
||||||
grunt.registerTask('test', ['lint', 'concat', 'simplemocha']);
|
grunt.registerTask('test', ['lint', 'concat', 'simplemocha']);
|
||||||
grunt.registerTask('test-without-building', ['simplemocha']);
|
grunt.registerTask('test-without-building', ['simplemocha']);
|
||||||
grunt.registerTask('build', ['lint', 'test', 'uglify']);
|
grunt.registerTask('build', ['test', 'uglify']);
|
||||||
grunt.registerTask('prep-release', ['build', 'changelog']);
|
grunt.registerTask('prep-release', ['build', 'changelog']);
|
||||||
|
|
||||||
// Default task(s).
|
// Default task(s).
|
||||||
|
|
BIN
dist/showdown.js
vendored
BIN
dist/showdown.js
vendored
Binary file not shown.
BIN
dist/showdown.js.map
vendored
BIN
dist/showdown.js.map
vendored
Binary file not shown.
BIN
dist/showdown.min.js
vendored
BIN
dist/showdown.min.js
vendored
Binary file not shown.
BIN
dist/showdown.min.js.map
vendored
BIN
dist/showdown.min.js.map
vendored
Binary file not shown.
|
@ -30,7 +30,7 @@
|
||||||
"url": "https://github.com/showdownjs/showdown.git",
|
"url": "https://github.com/showdownjs/showdown.git",
|
||||||
"web": "https://github.com/showdownjs/showdown"
|
"web": "https://github.com/showdownjs/showdown"
|
||||||
},
|
},
|
||||||
"license": "BSD",
|
"license": "BSD-2-Clause",
|
||||||
"main": "./dist/showdown.js",
|
"main": "./dist/showdown.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "grunt test"
|
"test": "grunt test"
|
||||||
|
|
|
@ -97,11 +97,12 @@ showdown.Converter = function (converterOptions) {
|
||||||
if (showdown.helper.isString(ext)) {
|
if (showdown.helper.isString(ext)) {
|
||||||
ext = showdown.helper.stdExtName(ext);
|
ext = showdown.helper.stdExtName(ext);
|
||||||
|
|
||||||
// TODO LEGACY SUPPORT CODE
|
// LEGACY_SUPPORT CODE
|
||||||
if (!showdown.helper.isUndefined(showdown.extensions[ext]) && showdown.extensions[ext]) {
|
if (showdown.extensions[ext]) {
|
||||||
console.warn(ext + ' is an old extension that uses a deprecated loading method.' +
|
console.warn('DEPRECATION WARNING: ' + ext + ' is an old extension that uses a deprecated loading method.' +
|
||||||
'Please inform the developer that the extension should be updated!');
|
'Please inform the developer that the extension should be updated!');
|
||||||
ext = showdown.extensions[ext];
|
legacyExtensionLoading(showdown.extensions[ext], ext);
|
||||||
|
return;
|
||||||
// END LEGACY SUPPORT CODE
|
// END LEGACY SUPPORT CODE
|
||||||
|
|
||||||
} else if (!showdown.helper.isUndefined(extensions[ext])) {
|
} else if (!showdown.helper.isUndefined(extensions[ext])) {
|
||||||
|
@ -110,21 +111,28 @@ showdown.Converter = function (converterOptions) {
|
||||||
} else {
|
} else {
|
||||||
throw Error('Extension "' + ext + '" could not be loaded. It was either not found or is not a valid extension.');
|
throw Error('Extension "' + ext + '" could not be loaded. It was either not found or is not a valid extension.');
|
||||||
}
|
}
|
||||||
} else if (typeof ext === 'function') {
|
}
|
||||||
|
|
||||||
|
if (typeof ext === 'function') {
|
||||||
ext = ext();
|
ext = ext();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!showdown.helper.isArray(ext)) {
|
||||||
|
ext = [ext];
|
||||||
|
}
|
||||||
|
|
||||||
if (!showdown.validateExtension(ext)) {
|
if (!showdown.validateExtension(ext)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (ext.type) {
|
for (var i = 0; i < ext.length; ++i) {
|
||||||
|
switch (ext[i].type) {
|
||||||
case 'lang':
|
case 'lang':
|
||||||
langExtensions.push(ext);
|
langExtensions.push(ext[i]);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'output':
|
case 'output':
|
||||||
outputModifiers.push(ext);
|
outputModifiers.push(ext[i]);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -132,6 +140,39 @@ showdown.Converter = function (converterOptions) {
|
||||||
throw Error('Extension loader error: Type unrecognized!!!');
|
throw Error('Extension loader error: Type unrecognized!!!');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* LEGACY_SUPPORT
|
||||||
|
* @param {*} ext
|
||||||
|
* @param {string} name
|
||||||
|
*/
|
||||||
|
function legacyExtensionLoading(ext, name) {
|
||||||
|
if (typeof ext === 'function') {
|
||||||
|
ext = ext(new showdown.Converter());
|
||||||
|
}
|
||||||
|
if (!showdown.helper.isArray(ext)) {
|
||||||
|
ext = [ext];
|
||||||
|
}
|
||||||
|
var valid = validate(ext, name);
|
||||||
|
|
||||||
|
if (!valid.valid) {
|
||||||
|
throw Error(valid.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < ext.length; ++i) {
|
||||||
|
switch (ext[i].type) {
|
||||||
|
case 'lang':
|
||||||
|
langExtensions.push(ext[i]);
|
||||||
|
break;
|
||||||
|
case 'output':
|
||||||
|
outputModifiers.push(ext[i]);
|
||||||
|
break;
|
||||||
|
default:// should never reach here
|
||||||
|
throw Error('Extension loader error: Type unrecognized!!!');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts a markdown string into HTML
|
* Converts a markdown string into HTML
|
||||||
|
@ -200,7 +241,6 @@ showdown.Converter = function (converterOptions) {
|
||||||
showdown.helper.forEach(outputModifiers, function (ext) {
|
showdown.helper.forEach(outputModifiers, function (ext) {
|
||||||
text = showdown.subParser('runExtension')(ext, text, options, globals);
|
text = showdown.subParser('runExtension')(ext, text, options, globals);
|
||||||
});
|
});
|
||||||
text = parsers.outputModifiers(text, options, globals);
|
|
||||||
|
|
||||||
return text;
|
return text;
|
||||||
};
|
};
|
||||||
|
@ -240,20 +280,34 @@ showdown.Converter = function (converterOptions) {
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove an extension from THIS converter
|
* Use a global registered extension with THIS converter
|
||||||
* @param {{}} extension
|
* @param {string} extensionName Name of the previously registered extension
|
||||||
|
*/
|
||||||
|
this.useExtension = function (extensionName) {
|
||||||
|
_parseExtension(extensionName);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove an extension from THIS converter.
|
||||||
|
* Note: This is a costly operation. It's better to initialize a new converter
|
||||||
|
* and specify the extensions you wish to use
|
||||||
|
* @param {Array} extension
|
||||||
*/
|
*/
|
||||||
this.removeExtension = function (extension) {
|
this.removeExtension = function (extension) {
|
||||||
|
if (!showdown.helper.isArray(extension)) {
|
||||||
|
extension = [extension];
|
||||||
|
}
|
||||||
|
for (var a = 0; a < extension.length; ++a) {
|
||||||
|
var ext = extension[a];
|
||||||
for (var i = 0; i < langExtensions.length; ++i) {
|
for (var i = 0; i < langExtensions.length; ++i) {
|
||||||
if (langExtensions[i] === extension) {
|
if (langExtensions[i] === ext) {
|
||||||
langExtensions[i].splice(i, 1);
|
langExtensions[i].splice(i, 1);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (var ii = 0; ii < outputModifiers.length; ++i) {
|
for (var ii = 0; ii < outputModifiers.length; ++i) {
|
||||||
if (outputModifiers[ii] === extension) {
|
if (outputModifiers[ii] === ext) {
|
||||||
outputModifiers[ii].splice(i, 1);
|
outputModifiers[ii].splice(i, 1);
|
||||||
return;
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -113,10 +113,16 @@ showdown.extension = function (name, ext) {
|
||||||
|
|
||||||
// Setter
|
// Setter
|
||||||
} else {
|
} else {
|
||||||
|
// Expand extension if it's wrapped in a function
|
||||||
if (typeof ext === 'function') {
|
if (typeof ext === 'function') {
|
||||||
ext = ext();
|
ext = ext();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ensure extension is an array
|
||||||
|
if (!showdown.helper.isArray(ext)) {
|
||||||
|
ext = [ext];
|
||||||
|
}
|
||||||
|
|
||||||
var validExtension = validate(ext, name);
|
var validExtension = validate(ext, name);
|
||||||
|
|
||||||
if (validExtension.valid) {
|
if (validExtension.valid) {
|
||||||
|
@ -155,22 +161,29 @@ showdown.resetExtensions = function () {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validate extension
|
* Validate extension
|
||||||
* @param {object} ext
|
* @param {array} extension
|
||||||
* @param {string} name
|
* @param {string} name
|
||||||
* @returns {{valid: boolean, error: string}}
|
* @returns {{valid: boolean, error: string}}
|
||||||
*/
|
*/
|
||||||
function validate(ext, name) {
|
function validate(extension, name) {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var baseMsg = (name) ? 'Error in ' + name + ' extension: ' : 'Error in unnamed extension',
|
var errMsg = (name) ? 'Error in ' + name + ' extension->' : 'Error in unnamed extension',
|
||||||
ret = {
|
ret = {
|
||||||
valid: true,
|
valid: true,
|
||||||
error: baseMsg
|
error: ''
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (!showdown.helper.isArray(extension)) {
|
||||||
|
extension = [extension];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < extension.length; ++i) {
|
||||||
|
var baseMsg = errMsg + 'sub-extension ' + i + ': ',
|
||||||
|
ext = extension[i];
|
||||||
if (typeof ext !== 'object') {
|
if (typeof ext !== 'object') {
|
||||||
ret.valid = false;
|
ret.valid = false;
|
||||||
ret.error = baseMsg + 'it must be an object, but ' + typeof ext + ' given';
|
ret.error = baseMsg + 'must be an object, but ' + typeof ext + ' given';
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -231,7 +244,7 @@ function validate(ext, name) {
|
||||||
ret.error = baseMsg + 'output extensions must define a filter property';
|
ret.error = baseMsg + 'output extensions must define a filter property';
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,11 +0,0 @@
|
||||||
/**
|
|
||||||
* Run language extensions
|
|
||||||
*/
|
|
||||||
showdown.subParser('outputModifiers', function (text, config, globals) {
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
showdown.helper.forEach(globals.outputModifiers, function (ext) {
|
|
||||||
text = showdown.subParser('runExtension')(ext, text);
|
|
||||||
});
|
|
||||||
return text;
|
|
||||||
});
|
|
24
test/node/legacyExtensionSupport.js
Normal file
24
test/node/legacyExtensionSupport.js
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
/**
|
||||||
|
* Created by Estevao on 06-06-2015.
|
||||||
|
*/
|
||||||
|
require('source-map-support').install();
|
||||||
|
var expect = require('chai').expect,
|
||||||
|
showdown = require('../../dist/showdown.js');
|
||||||
|
|
||||||
|
describe('showdown legacy extension support', function () {
|
||||||
|
'use strict';
|
||||||
|
var extObjMock =
|
||||||
|
{
|
||||||
|
type: 'lang',
|
||||||
|
filter: function () {}
|
||||||
|
},
|
||||||
|
extFunc = function () {
|
||||||
|
return [extObjMock];
|
||||||
|
};
|
||||||
|
|
||||||
|
it('accept extensions loaded by the old mechanism', function () {
|
||||||
|
showdown.extensions.bazinga = extFunc;
|
||||||
|
var cnv = new showdown.Converter({extensions: ['bazinga']});
|
||||||
|
expect(cnv.getAllExtensions().language).to.eql([extObjMock]);
|
||||||
|
});
|
||||||
|
});
|
|
@ -49,11 +49,11 @@ describe('showdown.Converter', function () {
|
||||||
converter.getAllExtensions().language.should.contain(extObjMock);
|
converter.getAllExtensions().language.should.contain(extObjMock);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('addExtension() should add a previous registered extension in showdown', function () {
|
it('useExtension() should use a previous registered extension in showdown', function () {
|
||||||
showdown.extension('foo', extObjMock);
|
showdown.extension('foo', extObjMock);
|
||||||
var converter = new showdown.Converter();
|
var converter = new showdown.Converter();
|
||||||
|
|
||||||
converter.addExtension('foo');
|
converter.useExtension('foo');
|
||||||
converter.getAllExtensions().language.should.contain(extObjMock);
|
converter.getAllExtensions().language.should.contain(extObjMock);
|
||||||
showdown.resetExtensions();
|
showdown.resetExtensions();
|
||||||
});
|
});
|
||||||
|
|
|
@ -16,7 +16,7 @@ describe('showdown.options', function () {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('showdown.extension', function () {
|
describe('showdown.extension()', function () {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var extObjMock = {
|
var extObjMock = {
|
||||||
|
@ -28,15 +28,15 @@ describe('showdown.extension', function () {
|
||||||
};
|
};
|
||||||
|
|
||||||
describe('should register', function () {
|
describe('should register', function () {
|
||||||
it('should register an extension object', function () {
|
it('an extension object', function () {
|
||||||
showdown.extension('foo', extObjMock);
|
showdown.extension('foo', extObjMock);
|
||||||
showdown.extension('foo').should.equal(extObjMock);
|
showdown.extension('foo').should.eql([extObjMock]);
|
||||||
showdown.resetExtensions();
|
showdown.resetExtensions();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should register an extension function', function () {
|
it('an extension function', function () {
|
||||||
showdown.extension('foo', extObjFunc);
|
showdown.extension('foo', extObjFunc);
|
||||||
showdown.extension('foo').should.equal(extObjMock);
|
showdown.extension('foo').should.eql([extObjMock]);
|
||||||
showdown.resetExtensions();
|
showdown.resetExtensions();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -78,6 +78,6 @@ describe('showdown.getAllExtensions()', function () {
|
||||||
|
|
||||||
it('should return all extensions', function () {
|
it('should return all extensions', function () {
|
||||||
showdown.extension('bar', extObjMock);
|
showdown.extension('bar', extObjMock);
|
||||||
showdown.getAllExtensions().should.eql({bar: extObjMock});
|
showdown.getAllExtensions().should.eql({bar: [extObjMock]});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue
Block a user