├── .gitignore ├── __tests__ ├── fixtures │ ├── stripped.html │ ├── no-doctype.html │ ├── barebones.html │ ├── bins │ │ ├── css-slurp.json │ │ ├── css-slurp.html │ │ ├── module.json │ │ ├── module.html │ │ ├── og.html │ │ └── og.json │ ├── simple.html │ ├── protocol.html │ ├── messedup.html │ ├── simple.js │ └── simple.css ├── .eslintrc.js ├── empty.test.js ├── html-only.test.js ├── bins.test.js ├── multi-run.test.js ├── after-line.js ├── integrity.test.js ├── single-field-only.test.js ├── full.test.js ├── protocol.test.js └── insert.test.js ├── README.md ├── .travis.yml ├── LICENSE.md ├── package.json └── lib └── index.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .vscode 3 | dist 4 | -------------------------------------------------------------------------------- /__tests__/fixtures/stripped.html: -------------------------------------------------------------------------------- 1 | 2 |

Hello world

3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | bin-to-file 2 | =========== 3 | 4 | Converts JS Bin object to a single file 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | notifications: 3 | email: false 4 | before_script: 5 | - npm install 6 | node_js: 7 | - 8 8 | -------------------------------------------------------------------------------- /__tests__/fixtures/no-doctype.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Just a simple example 5 | 6 | 7 |

Nicely done sir.

8 | 9 | -------------------------------------------------------------------------------- /__tests__/.eslintrc.js: -------------------------------------------------------------------------------- 1 | const res = Object.assign({}, require('@remy/eslint/jest'), { 2 | parserOptions: { 3 | sourceType: 'module', 4 | ecmaVersion: 8, 5 | }, 6 | }); 7 | 8 | module.exports = res; 9 | -------------------------------------------------------------------------------- /__tests__/fixtures/barebones.html: -------------------------------------------------------------------------------- 1 | 2 | Just a simple example 3 | 4 | 5 |

Nicely done sir.

-------------------------------------------------------------------------------- /__tests__/fixtures/bins/css-slurp.json: -------------------------------------------------------------------------------- 1 | { 2 | "html": "घर घर घर घर\n## Links: [Netflix](netflix.com)\n", 3 | "javascript": "\n//# sourceURL=holy-smoke-F0C.js", 4 | "css": "body {\n background: blue;\n}" 5 | } 6 | -------------------------------------------------------------------------------- /__tests__/fixtures/simple.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Just a simple example 7 | 8 | 9 | 10 |

Nicely done sir.

11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /__tests__/fixtures/bins/css-slurp.html: -------------------------------------------------------------------------------- 1 | घर घर घर घर 2 | 7 | 8 | ## Links: [Netflix](netflix.com) 9 | 10 | 11 | 15 | -------------------------------------------------------------------------------- /__tests__/fixtures/protocol.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /__tests__/fixtures/messedup.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | JS Bin</t</head> 7 | <body> 8 | <div id="readme"> 9 | <article class="markdown-body"> 10 | <h1> 11 | <a name="how-to-find-the-pin-url" class="anchor" href="#how-to-find-the-pin-url"><span class="mini-icon mini-icon-link"></span></a>How to find for the Pin URL</h1> -------------------------------------------------------------------------------- /__tests__/fixtures/simple.js: -------------------------------------------------------------------------------- 1 | var object = { 2 | downloadBin: function(req, res, next) { 3 | this.protectVisibility(req.session.user, req.bin, function(err, bin) { 4 | var filename = ['jsbin', bin.url, bin.revision, 'html'].join('.'); 5 | 6 | var data = { 7 | domain: helpers.set('url host'), 8 | permalink: helpers.editUrlForBin(bin, true), 9 | user: undefsafe(bin, 'metadata.name') || false, 10 | year: new Date().getYear() + 1900, 11 | }; 12 | }); 13 | }, 14 | }; 15 | -------------------------------------------------------------------------------- /__tests__/empty.test.js: -------------------------------------------------------------------------------- 1 | var toFile = require('../lib'); 2 | var fs = require('fs'); 3 | var path = require('path'); 4 | 5 | var html = ''; 6 | 7 | beforeEach(function() { 8 | html = fs.readFileSync( 9 | path.join(__dirname, 'fixtures', 'simple.html'), 10 | 'utf8' 11 | ); 12 | }); 13 | 14 | test('should insert JS at end when missing </body>', function() { 15 | var file = toFile({ html: html, javascript: '', css: '' }); 16 | 17 | file = file.replace(/<!--hash:.*?-->\n/, ''); 18 | 19 | expect(file).toEqual(html); 20 | }); 21 | -------------------------------------------------------------------------------- /__tests__/html-only.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /*global describe, it, beforeEach */ 3 | var assert = require('assert'); 4 | var toFile = require('../lib'); 5 | var fs = require('fs'); 6 | var path = require('path'); 7 | 8 | describe('simple html', function() { 9 | var html = ''; 10 | 11 | beforeEach(function() { 12 | html = fs.readFileSync( 13 | path.join(__dirname, 'fixtures', 'simple.html'), 14 | 'utf8' 15 | ); 16 | }); 17 | 18 | it('should have fixtures', function() { 19 | assert(html.length > 0, 'fixture HTML has content'); 20 | }); 21 | 22 | it('does nothing to plain html', function() { 23 | var file = toFile({ html: html }); 24 | 25 | file = file.replace(/<!--hash:.*?-->\n/, ''); 26 | 27 | assert(html === file, 'content matches'); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /__tests__/bins.test.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const readdir = fs.readdirSync; 3 | const { promisify } = require('util'); 4 | const readFile = promisify(fs.readFile); 5 | const base = __dirname + '/fixtures/bins'; 6 | const fixtures = readdir(base); 7 | const binToFile = require('../lib'); 8 | 9 | describe('bin fixtures', () => { 10 | fixtures 11 | .filter(f => f.endsWith('.json')) 12 | .map(f => [`${base}/${f}`, f]) 13 | .forEach(([filename, fixture]) => { 14 | it(fixture, async () => { 15 | const bin = await readFile(filename, 'utf8'); 16 | const html = binToFile(JSON.parse(bin)); 17 | 18 | const expecting = await readFile( 19 | filename.replace(/\.json$/, '.html'), 20 | 'utf8' 21 | ); 22 | expect(html).toEqual(expecting.trim()); 23 | }); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /__tests__/fixtures/bins/module.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": 41972605, 3 | "url": "white-art-631", 4 | "revision": 1, 5 | "active": "y", 6 | "javascript": 7 | "import {\n render,\n html\n} from 'https://unpkg.com/lit-html@0.9.0/lit-html.js';\n\n// A lit-html template uses the `html` template tag:\nlet sayHello = (name) => html `<h1>Hello ${name}</h1>`;\n\n// It's rendered with the `render()` function:\nrender(sayHello('World'), document.body);\n\nsetTimeout(() => {\n // And re-renders only update the data that changed, without\n // VDOM diffing!\n render(sayHello('Everyone'), document.body);\n}, 500);", 8 | "html": 9 | "<!DOCTYPE html>\n<html>\n<head>\n <meta charset=\"utf-8\">\n <meta name=\"viewport\" content=\"width=device-width\">\n <title>lit demo\n\n\n\n\n", 10 | "css": "* {\n font-family: sans-serif;\n}", 11 | "settings": "{}" 12 | } 13 | -------------------------------------------------------------------------------- /__tests__/fixtures/bins/module.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | lit demo 8 | 13 | 14 | 15 | 16 | 17 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /__tests__/multi-run.test.js: -------------------------------------------------------------------------------- 1 | /* eslint-env jest */ 2 | const toFile = require('../lib'); 3 | const fileToBin = require('file-to-bin'); 4 | const fs = require('fs'); 5 | const path = require('path'); 6 | 7 | describe('stripped and repeatedly converted', function() { 8 | var html = ''; 9 | 10 | beforeEach(function() { 11 | html = fs.readFileSync( 12 | path.join(__dirname, 'fixtures', 'stripped.html'), 13 | 'utf8' 14 | ); 15 | }); 16 | 17 | it('should have fixtures', function() { 18 | expect(html.length).toBeGreaterThan(0); 19 | }); 20 | 21 | it('does nothing to plain html', function() { 22 | const bin = fileToBin(html); 23 | bin.url = 'abc-def-ghi'; 24 | // bin.meta = ` 25 | // `; 26 | const file = toFile(bin); 27 | const parsed = fileToBin(file); 28 | 29 | expect(parsed.html).toEqual(bin.html); 30 | expect(parsed.url).toEqual(bin.url); 31 | 32 | expect(toFile(parsed)).toEqual(toFile(bin)); 33 | }); 34 | }); 35 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 JS Bin Ltd 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /__tests__/after-line.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | const { afterLine } = require('../lib/'); 3 | 4 | test('inserts below html', () => { 5 | const html = ` 7 | 8 | 9 | 10 | Sandbox 11 | 12 | 16 | `; 17 | 18 | const meta = ` 25 | 26 | `; 27 | 28 | const res = afterLine(html, ' 0) { 44 | throw { error: 'html errors have been found', results: res }; 45 | } 46 | done(); 47 | }, 48 | }); 49 | }); 50 | }); 51 | -------------------------------------------------------------------------------- /__tests__/fixtures/simple.css: -------------------------------------------------------------------------------- 1 | /*#panels a:nth-child(4) { 2 | display: none; 3 | } 4 | #panels a:nth-child(3).active + a , 5 | #panels a:nth-child(4).active { 6 | display: inline-block; 7 | }*/ 8 | 9 | /* nav */ 10 | #control, 11 | .control { 12 | background: rgb(237, 237, 237); 13 | background: hsl(0, 0%, 93%); 14 | border-bottom: rgb(191, 191, 191) solid 1px; 15 | border-bottom: hsl(0, 0%, 75%) solid 1px; 16 | } 17 | #control *, 18 | .control * { 19 | text-shadow: none; 20 | /* font-family: opensans; */ 21 | } 22 | 23 | /* toggles */ 24 | .hasContent { 25 | font-weight: bold; 26 | background: none; 27 | } 28 | .hasContent:after { 29 | display: none; 30 | } 31 | .hasContent.active { 32 | font-weight: normal; 33 | } 34 | 35 | /* thick line at the top of the panel */ 36 | .panel:before, 37 | .focus.panel:before { 38 | background: none !important; 39 | } 40 | 41 | /*panel backgrounds*/ 42 | .panel { 43 | background: rgb(247, 247, 247); 44 | background: hsl(0, 0%, 97%); 45 | } 46 | .panel.focus { 47 | background: #fff; 48 | } 49 | /*panel borders*/ 50 | .stretch.panelwrapper { 51 | border-left-color: rgb(230, 230, 230) !important; 52 | border-left-color: hsl(60, 0%, 90%) !important; 53 | background: white !important; 54 | } 55 | /* panel borders hovered */ 56 | .resize:hover + .stretch.panelwrapper { 57 | border-left-color: #39f !important; 58 | border-left-style: dashed !important; 59 | } 60 | .resize { 61 | cursor:move !important; 62 | } 63 | -------------------------------------------------------------------------------- /__tests__/single-field-only.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /*global describe, it, beforeEach */ 3 | var assert = require('assert'); 4 | var toFile = require('../lib'); 5 | var fs = require('fs'); 6 | var path = require('path'); 7 | var cheerio = require('cheerio'); 8 | 9 | describe('js only', function() { 10 | var js = ''; 11 | 12 | beforeEach(function() { 13 | js = fs.readFileSync(path.join(__dirname, 'fixtures', 'simple.js'), 'utf8'); 14 | }); 15 | 16 | it('should have fixtures', function() { 17 | assert(js.length > 0, 'fixture js has content'); 18 | }); 19 | 20 | it('does nothing to plain js', function() { 21 | var file = toFile({ javascript: js, url: 'foo', revision: 10 }); 22 | 23 | var $ = cheerio.load(file); 24 | var outputJS = $('#jsbin-javascript').text(); 25 | 26 | assert(js.trim() === outputJS.trim(), 'content matches'); 27 | }); 28 | }); 29 | 30 | describe('css only', function() { 31 | var css = ''; 32 | 33 | beforeEach(function() { 34 | css = fs.readFileSync( 35 | path.join(__dirname, 'fixtures', 'simple.css'), 36 | 'utf8' 37 | ); 38 | }); 39 | 40 | it('should have fixtures', function() { 41 | assert(css.length > 0, 'fixture css has content'); 42 | }); 43 | 44 | it('does nothing to plain css', function() { 45 | var file = toFile({ css: css }); 46 | 47 | var $ = cheerio.load(file); 48 | var outputCSS = $('#jsbin-css').text(); 49 | 50 | assert(outputCSS.trim() === css.trim(), 'content matches'); 51 | }); 52 | }); 53 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bin-to-file", 3 | "version": "1.0.8", 4 | "description": "Converts JS Bin object to static HTML", 5 | "main": "dist/index.js", 6 | "babel": { 7 | "presets": [ 8 | "env", 9 | "stage-0" 10 | ], 11 | "plugins": [] 12 | }, 13 | "directories": { 14 | "lib": "lib", 15 | "test": "test" 16 | }, 17 | "files": [ 18 | "dist" 19 | ], 20 | "scripts": { 21 | "test": "jest **/*.test.js", 22 | "prepublishOnly": "babel --ignore=node_modules lib/index.js -o dist/index.js" 23 | }, 24 | "repository": { 25 | "type": "git", 26 | "url": "git://github.com/jsbin/bin-to-file.git" 27 | }, 28 | "keywords": [ 29 | "jsbin" 30 | ], 31 | "author": "Remy Sharp", 32 | "bugs": { 33 | "url": "https://github.com/jsbin/bin-to-file/issues" 34 | }, 35 | "homepage": "https://github.com/jsbin/bin-to-file", 36 | "devDependencies": { 37 | "@remy/eslint": "^1.3.0", 38 | "babel-cli": "^6.26.0", 39 | "babel-eslint": "^7.2.3", 40 | "babel-preset-env": "^1.6.1", 41 | "babel-preset-stage-0": "^6.24.1", 42 | "cheerio": "^0.19.0", 43 | "eslint": "^4.16.0", 44 | "eslint-config-react-app": "^2.1.0", 45 | "eslint-plugin-flowtype": "^2.41.0", 46 | "eslint-plugin-import": "^2.8.0", 47 | "eslint-plugin-jest": "^21.7.0", 48 | "eslint-plugin-jsx-a11y": "^5.1.1", 49 | "eslint-plugin-node": "^5.2.1", 50 | "eslint-plugin-react": "^7.5.1", 51 | "file-to-bin": "^1.0.0", 52 | "jest": "^22.1.4", 53 | "w3cjs": "~0.1.25" 54 | }, 55 | "dependencies": {}, 56 | "license": "MIT" 57 | } 58 | -------------------------------------------------------------------------------- /__tests__/full.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /*global describe, it, beforeEach */ 3 | var assert = require('assert'); 4 | var toFile = require('../lib'); 5 | var fs = require('fs'); 6 | var path = require('path'); 7 | var cheerio = require('cheerio'); 8 | 9 | describe('full bin insert', function() { 10 | var html = ''; 11 | 12 | beforeEach(function() { 13 | html = fs.readFileSync( 14 | path.join(__dirname, 'fixtures', 'simple.html'), 15 | 'utf8' 16 | ); 17 | }); 18 | 19 | it('should also store source panel content', function() { 20 | var javascript = 'alert("Hello world");'; 21 | var css = 'body { background: red; }'; 22 | var meta = ''; 23 | var modifiedHTML = html + '\n'; 24 | var file = toFile({ 25 | html: html, 26 | javascript: javascript, 27 | css: css, 28 | meta: meta, 29 | source: { 30 | html: modifiedHTML, 31 | javascript: javascript + '\n// tested', 32 | css: css + '\n/* tested */', 33 | }, 34 | }); 35 | 36 | var $ = cheerio.load(file, { xmlMode: false, decodeEntities: false }); 37 | var sourceHTML = $('#jsbin-source-html').text(); 38 | 39 | assert( 40 | sourceHTML.indexOf('<\\!-- tested -->') !== -1, 41 | 'source HTML is present' 42 | ); 43 | 44 | $ = cheerio.load( 45 | sourceHTML.replace(/<\\!--/, ' 5 | 6 | 7 | Sandbox 8 | 9 | 30 | 31 | 32 |
33 |
34 |
title title title title
35 |
more
text
more
text
...........................
36 |
37 |
38 |
39 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /__tests__/fixtures/bins/og.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": 622, 3 | "url": "aseno", 4 | "revision": 1, 5 | "active": "y", 6 | "javascript": 7 | "/**\r\n * note: I've removed the .stop() because if the mouse hovers back\r\n * over the div before the effect has completed, it changes the \r\n * original 'height' of the div, and the next time it slides down\r\n * it won't slide down to the correct height.\r\n */\r\n \r\n $('#p1').hover(function(e) {\r\n // we're searching the children of 'this' rather than the event's target\r\n // which can be different each time\r\n $(this).children('.panelContent').slideDown('slow');\r\n }, function(e) {\r\n $(this).children('.panelContent').slideUp('slow');\r\n });", 8 | "html": 9 | "\r\n\r\n\r\n\r\nSandbox\r\n\r\n\r\n\r\n\r\n
\r\n
\r\n
title title title title
\r\n
more
text
more
text
...........................
\r\n
\r\n
\r\n
\r\n\r\n\r\n", 10 | "css": null, 11 | "created": "2008-10-13T12:20:58.000Z", 12 | "settings": null 13 | } 14 | -------------------------------------------------------------------------------- /__tests__/insert.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /*global describe, it, beforeEach */ 3 | var assert = require('assert'); 4 | var toFile = require('../lib'); 5 | var fs = require('fs'); 6 | const { promisify } = require('util'); 7 | const read = promisify(fs.readFile); 8 | var path = require('path'); 9 | 10 | function metadata({ 11 | url, 12 | revision, 13 | user = 'anonymous', 14 | year = new Date().getFullYear(), 15 | }) { 16 | return ` 23 | `; 24 | } 25 | 26 | describe('content insert', function() { 27 | var html = ''; 28 | 29 | beforeEach(function() { 30 | html = fs.readFileSync( 31 | path.join(__dirname, 'fixtures', 'simple.html'), 32 | 'utf8' 33 | ); 34 | }); 35 | 36 | it('should insert JS before the closing body', function() { 37 | var javascript = 'alert("Hello world");'; 38 | var file = toFile({ html, javascript }); 39 | 40 | assert(file.indexOf(javascript) !== -1, 'contains the javascript: ' + file); 41 | 42 | var lines = file.split('\n'); 43 | var pos = lines.findIndex(line => line.includes(javascript)); 44 | assert(lines[pos + 2].indexOf('') === 0); 45 | }); 46 | 47 | it('should insert CSS before the closing head', function() { 48 | var css = 'body { background: red; }'; 49 | var file = toFile({ html: html, css: css }); 50 | 51 | assert(file.indexOf(css) !== -1, 'contains the css: ' + file); 52 | 53 | var lines = file.split('\n'); 54 | var pos = lines.indexOf(css); 55 | assert(lines[pos + 2].indexOf('') === 0, lines[pos + 2]); 56 | }); 57 | 58 | it('should load after ', function() { 59 | var html = fs.readFileSync( 60 | path.join(__dirname, 'fixtures', 'barebones.html'), 61 | 'utf8' 62 | ); 63 | var css = 'body { background: red; }'; 64 | var file = toFile({ html: html, css: css }); 65 | 66 | expect(file).toEqual(expect.stringContaining(css)); 67 | expect(file).toEqual(expect.stringContaining('')); 68 | }); 69 | 70 | it('should insert JS at end when missing ', function() { 71 | var html = fs.readFileSync( 72 | path.join(__dirname, 'fixtures', 'barebones.html'), 73 | 'utf8' 74 | ); 75 | var javascript = 'alert("Hello world");'; 76 | var file = toFile({ html: html, javascript: javascript }); 77 | 78 | assert(file.indexOf(javascript) !== -1, 'contains the javascript: ' + file); 79 | 80 | var lines = file.split('\n'); 81 | 82 | assert(lines.slice(-1)[0] === '', '???: ' + lines.slice(-2)); 83 | }); 84 | }); 85 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | const doctypeRe = new RegExp(/^]*>\n?/im); 2 | 3 | function afterLine(source, needle, value) { 4 | if (!source.toLowerCase().includes(needle.toLowerCase())) { 5 | return null; 6 | } 7 | 8 | let found = false; 9 | const res = source 10 | .split('\n') 11 | .reduce((acc, curr) => { 12 | acc.push(curr); 13 | if ( 14 | !found && 15 | curr 16 | .trim() 17 | .toLowerCase() 18 | .startsWith(needle) 19 | ) { 20 | acc.push(value.trim()); 21 | found = true; 22 | } 23 | 24 | return acc; 25 | }, []) 26 | .join('\n'); 27 | 28 | return found ? res : null; 29 | } 30 | 31 | function insert(source, needle, value, after = false) { 32 | needle = needle.toLowerCase(); 33 | const sourceLC = source.toLowerCase(); 34 | if (!sourceLC.includes(needle)) { 35 | return null; 36 | } 37 | 38 | let left = source.substring(0, sourceLC.lastIndexOf(needle)); 39 | 40 | let ri = sourceLC.lastIndexOf(needle); 41 | 42 | if (after) { 43 | ri += needle.length; 44 | left += needle + '\n'; 45 | } 46 | 47 | const right = source.substring(ri); 48 | 49 | if (left && right) { 50 | return left + value + right; 51 | } 52 | return ''; 53 | } 54 | 55 | function safeForHTML(s) { 56 | return (s || '') 57 | .replace(/<\/script>/gi, '<\\/script>') 58 | .replace(/\n${meta.trim()}\n\n`; 139 | const afterHTML = afterLine(file, '\n${javascript}\n`; 170 | 171 | const body = insert(file, '', javascript + '\n'); 172 | if (body) { 173 | file = body; 174 | } else { 175 | // slap on the bottom 176 | file = `${file}\n${javascript}`; 177 | } 178 | } 179 | } 180 | 181 | // If we have the raw panel content - go ahead and stick that in scripts at the bottom. 182 | if (source) { 183 | if (source.css === css) { 184 | delete source.css; 185 | } 186 | if (source.javascript === javascript) { 187 | delete source.javascript; 188 | } 189 | if (source.html === html) { 190 | delete source.html; 191 | } 192 | 193 | const sourceScripts = ['html', 'css', 'javascript'] 194 | .map(type => { 195 | if (source[type] === undefined) { 196 | return ''; 197 | } 198 | 199 | const content = safeForHTML(source[type]); 200 | if (content) { 201 | return `\n`; 204 | } 205 | 206 | return ''; 207 | }) 208 | .join(''); 209 | 210 | const bodyTag = insert(file, '', sourceScripts); 211 | if (bodyTag) { 212 | file = bodyTag; 213 | } else { 214 | file += sourceScripts; 215 | } 216 | } 217 | 218 | return file; 219 | } 220 | 221 | if (typeof module !== 'undefined') { 222 | module.exports = binToFile; 223 | module.exports.afterLine = afterLine; 224 | if (!module.parent) { 225 | // cli mode 226 | if (process.stdin && !process.stdin.isTTY) { 227 | const stdinBuffer = require('fs').readFileSync(0); // STDIN_FILENO = 0 228 | console.log(module.exports(JSON.parse(stdinBuffer.toString()))); 229 | } 230 | } 231 | } 232 | --------------------------------------------------------------------------------