diff --git a/ghost/tpl/.eslintrc.js b/ghost/tpl/.eslintrc.js new file mode 100644 index 0000000000..c9c1bcb522 --- /dev/null +++ b/ghost/tpl/.eslintrc.js @@ -0,0 +1,6 @@ +module.exports = { + plugins: ['ghost'], + extends: [ + 'plugin:ghost/node' + ] +}; diff --git a/ghost/tpl/LICENSE b/ghost/tpl/LICENSE new file mode 100644 index 0000000000..366ae5f624 --- /dev/null +++ b/ghost/tpl/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2013-2021 Ghost Foundation + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/ghost/tpl/README.md b/ghost/tpl/README.md new file mode 100644 index 0000000000..9c294e13db --- /dev/null +++ b/ghost/tpl/README.md @@ -0,0 +1,39 @@ +# Tpl + +## Install + +`npm install @tryghost/tpl --save` + +or + +`yarn add @tryghost/tpl` + + +## Usage + + +## Develop + +This is a mono repository, managed with [lerna](https://lernajs.io/). + +Follow the instructions for the top-level repo. +1. `git clone` this repo & `cd` into it as usual +2. Run `yarn` to install top-level dependencies. + + +## Run + +- `yarn dev` + + +## Test + +- `yarn lint` run just eslint +- `yarn test` run lint and tests + + + + +# Copyright & License + +Copyright (c) 2013-2021 Ghost Foundation - Released under the [MIT license](LICENSE). \ No newline at end of file diff --git a/ghost/tpl/index.js b/ghost/tpl/index.js new file mode 100644 index 0000000000..bcbd8ac9bc --- /dev/null +++ b/ghost/tpl/index.js @@ -0,0 +1 @@ +module.exports = require('./lib/tpl'); diff --git a/ghost/tpl/lib/tpl.js b/ghost/tpl/lib/tpl.js new file mode 100644 index 0000000000..11dc1281e8 --- /dev/null +++ b/ghost/tpl/lib/tpl.js @@ -0,0 +1,28 @@ +const template = require('lodash.template'); + +const interpolate = /(? { + if (!data) { + return string; + } + + // We replace any escaped left braces with the unicode character so we can swap it back later + let processedString = string.replace(/\\{/g, '\\U+007B'); + // Let lodash do its thing + processedString = template(processedString, {interpolate})(data); + // Replace our swapped out left braces and any escaped right braces + return processedString.replace(/\\U\+007B/g, '{').replace(/\\}/g, '}'); +}; diff --git a/ghost/tpl/package.json b/ghost/tpl/package.json new file mode 100644 index 0000000000..fc0b1008c7 --- /dev/null +++ b/ghost/tpl/package.json @@ -0,0 +1,29 @@ +{ + "name": "@tryghost/tpl", + "version": "0.0.0", + "repository": "https://github.com/TryGhost/Utils/tree/master/packages/tpl", + "author": "Ghost Foundation", + "license": "MIT", + "main": "index.js", + "scripts": { + "dev": "echo \"Implement me!\"", + "test": "NODE_ENV=testing mocha './test/**/*.test.js'", + "lint": "eslint . --ext .js --cache", + "posttest": "yarn lint" + }, + "files": [ + "index.js", + "lib" + ], + "publishConfig": { + "access": "public" + }, + "devDependencies": { + "mocha": "9.0.0", + "should": "13.2.3", + "sinon": "11.1.1" + }, + "dependencies": { + "lodash.template": "^4.5.0" + } +} diff --git a/ghost/tpl/test/.eslintrc.js b/ghost/tpl/test/.eslintrc.js new file mode 100644 index 0000000000..829b601eb0 --- /dev/null +++ b/ghost/tpl/test/.eslintrc.js @@ -0,0 +1,6 @@ +module.exports = { + plugins: ['ghost'], + extends: [ + 'plugin:ghost/test' + ] +}; diff --git a/ghost/tpl/test/lodash.test.js b/ghost/tpl/test/lodash.test.js new file mode 100644 index 0000000000..8208af5cd4 --- /dev/null +++ b/ghost/tpl/test/lodash.test.js @@ -0,0 +1,12 @@ +// Switch these lines once there are useful utils +// const testUtils = require('./utils'); +require('./utils'); + +describe('Lodash Template', function () { + it('Does not get clobbered by this lib', function () { + require('../lib/tpl'); + let _ = require('lodash'); + + _.templateSettings.interpolate.should.eql(/<%=([\s\S]+?)%>/g); + }); +}); diff --git a/ghost/tpl/test/tpl.test.js b/ghost/tpl/test/tpl.test.js new file mode 100644 index 0000000000..c82a39e60f --- /dev/null +++ b/ghost/tpl/test/tpl.test.js @@ -0,0 +1,74 @@ +// Switch these lines once there are useful utils +// const testUtils = require('./utils'); +require('./utils'); +const tpl = require('../'); + +describe('tpl', function () { + it('Can handle a plain string', function () { + const string = 'My template'; + const result = tpl(string); + + result.should.eql('My template'); + }); + + it('Can handle a string with data', function () { + const string = 'Go visit {url}'; + const data = {url: 'https://example.com'}; + + let result = tpl(string, data); + + result.should.eql('Go visit https://example.com'); + }); + + it('Can handle mixing with handlebars-related messages', function () { + const string = '{{#get}} helper took {totalMs}ms to complete'; + const data = { + totalMs: '500' + }; + + let result = tpl(string, data); + result.should.eql('{{#get}} helper took 500ms to complete'); + }); + + it('Can handle escaped left braces', function () { + const string = 'The \\{\\{{helperName}}} helper is not available.'; + const data = { + helperName: 'get', + totalMs: '500' + }; + let result = tpl(string, data); + result.should.eql('The {{get}} helper is not available.'); + }); + + it('Can handle escaped right braces as well', function () { + const string = 'The \\{\\{{helperName}\\}\\} helper is not available.'; + const data = { + helperName: 'get', + totalMs: '500' + }; + let result = tpl(string, data); + result.should.eql('The {{get}} helper is not available.'); + }); + + it('has a simple bare minimum escaping needed', function () { + const string = 'The {\\{{helperName}}} helper is not available.'; + const data = { + helperName: 'get', + totalMs: '500' + }; + let result = tpl(string, data); + result.should.eql('The {{get}} helper is not available.'); + }); + + it('Returns a sensible error if data is missing', function () { + const string = '{helperName} helper took {totalMs}ms to complete'; + const data = { + totalMs: '500' + }; + + let resultFn = () => { + tpl(string, data); + }; + resultFn.should.throw('helperName is not defined'); + }); +}); diff --git a/ghost/tpl/test/utils/assertions.js b/ghost/tpl/test/utils/assertions.js new file mode 100644 index 0000000000..7364ee8aa1 --- /dev/null +++ b/ghost/tpl/test/utils/assertions.js @@ -0,0 +1,11 @@ +/** + * Custom Should Assertions + * + * Add any custom assertions to this file. + */ + +// Example Assertion +// should.Assertion.add('ExampleAssertion', function () { +// this.params = {operator: 'to be a valid Example Assertion'}; +// this.obj.should.be.an.Object; +// }); diff --git a/ghost/tpl/test/utils/index.js b/ghost/tpl/test/utils/index.js new file mode 100644 index 0000000000..0d67d86ff8 --- /dev/null +++ b/ghost/tpl/test/utils/index.js @@ -0,0 +1,11 @@ +/** + * Test Utilities + * + * Shared utils for writing tests + */ + +// Require overrides - these add globals for tests +require('./overrides'); + +// Require assertions - adds custom should assertions +require('./assertions'); diff --git a/ghost/tpl/test/utils/overrides.js b/ghost/tpl/test/utils/overrides.js new file mode 100644 index 0000000000..90203424ee --- /dev/null +++ b/ghost/tpl/test/utils/overrides.js @@ -0,0 +1,10 @@ +// This file is required before any test is run + +// Taken from the should wiki, this is how to make should global +// Should is a global in our eslint test config +global.should = require('should').noConflict(); +should.extend(); + +// Sinon is a simple case +// Sinon is a global in our eslint test config +global.sinon = require('sinon');