diff --git a/.travis.yml b/.travis.yml index 5988ad9cb..826a00e14 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,7 +23,7 @@ after_success: before_deploy: - npm run build - - npm run docs + - ASSET_PATH=/thema/ npm run docs deploy: - provider: script diff --git a/package.json b/package.json index ed602062f..30275c03d 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ "build:browser": "webpack --mode production && tsc --emitDeclarationOnly --project tsconfig.browser.json", "build:lib": "tsc --project tsconfig.lib.json", "dev": "webpack-dev-server --mode development --hot --open", - "docs": "npm run build:lib && npm run docs:readme && npm run docs:gallery && npm run docs:app", + "docs": "npm run build:browser && npm run docs:readme && npm run docs:gallery && npm run docs:app", "docs:readme": "ts-node --files src/scripts/readme.ts", "docs:gallery": "ts-node --files src/scripts/gallery.ts", "docs:app": "webpack --mode production --env.docs=true", diff --git a/src/demo/app.tsx b/src/demo/app.tsx index 73e8019e4..92610415e 100644 --- a/src/demo/app.tsx +++ b/src/demo/app.tsx @@ -4,15 +4,6 @@ * Provides an interface for switching of both example and theme. * Used for human 🤗 user acceptance testing 👍 and robot 🤖 * visual regression testing. - * - * For HTML content and Javascript modules, - * switching is achieved via Parcel's [dynamic importing of modules] - * (https://parceljs.org/code_splitting.html). Note that this seems to load - * content for all examples and all themes i.e. it is not truly lazy. - * - * For CSS, this `import()` approach did not work, maybe because it loads all the - * CSS stylesheets into the global DOM. So, we take the approach of - * enabling/disabling `` elements. */ import React from 'react' diff --git a/src/demo/editor/header.tsx b/src/demo/editor/header.tsx index b23299ac6..ab6397b7c 100644 --- a/src/demo/editor/header.tsx +++ b/src/demo/editor/header.tsx @@ -3,6 +3,7 @@ import ReactDOM from 'react-dom' import { examples } from '../../examples' import { getExample, setExample } from '../utils/preview' import { ViewportToggle } from './viewportToggle' +import { ASSET_PATH } from '../utils' interface Props { exampleContent: string[] @@ -10,7 +11,7 @@ interface Props { export const HeaderBase = (): JSX.Element => { return ( - + Thema diff --git a/src/demo/styles.css b/src/demo/styles.css index 9cac33191..1d30cab04 100644 --- a/src/demo/styles.css +++ b/src/demo/styles.css @@ -134,7 +134,7 @@ main { text-transform: capitalize; } - & > a[href^='/editor?'] { + & > a[href*='editor?'] { order: -1; display: block; border-bottom: 1px solid var(--color-neutral-400); diff --git a/src/demo/templates/template.html b/src/demo/templates/template.html index 321174c54..87dd25fcd 100644 --- a/src/demo/templates/template.html +++ b/src/demo/templates/template.html @@ -47,7 +47,10 @@

Pull Request

- +
diff --git a/src/demo/utils/index.ts b/src/demo/utils/index.ts index 27f26f4ab..47be51259 100644 --- a/src/demo/utils/index.ts +++ b/src/demo/utils/index.ts @@ -1,5 +1,7 @@ import { translate } from '../../util' +export const ASSET_PATH = process.env.ASSET_PATH ?? '/' + export interface ThemeObject { [key: string]: string } diff --git a/src/demo/utils/preview.ts b/src/demo/utils/preview.ts index 7216580aa..ef45e0c06 100644 --- a/src/demo/utils/preview.ts +++ b/src/demo/utils/preview.ts @@ -1,4 +1,4 @@ -import { keys } from '.' +import { keys, ASSET_PATH } from '.' import { examples, resolveExample } from '../../examples' import { append, create } from '../../util' @@ -21,7 +21,10 @@ export const setExample = (example: string): void => { const preview = getPreview() if (preview !== null && !preview.getAttribute('src')?.includes(example)) { - preview.setAttribute('src', `/examples/${resolveExample(example)}.html`) + preview.setAttribute( + 'src', + `${ASSET_PATH}examples/${resolveExample(example)}.html` + ) } } diff --git a/src/demo/utils/theme.ts b/src/demo/utils/theme.ts index 37b09b1a6..7b2a18026 100644 --- a/src/demo/utils/theme.ts +++ b/src/demo/utils/theme.ts @@ -1,4 +1,4 @@ -import { diff, objToVars, ThemeObject } from '.' +import { diff, objToVars, ThemeObject, ASSET_PATH } from '.' import { styleEntry, themes } from '../../browser' import { append, create, prepend } from '../../util' import { keys } from './index' @@ -11,7 +11,7 @@ import { export const getThemeCSS = (theme: string): string => { const req = new XMLHttpRequest() - req.open('GET', `/themes/${theme}/styles.css`, false) + req.open('GET', `${ASSET_PATH}themes/${theme}/styles.css`, false) req.send(null) return req.responseText } @@ -35,7 +35,7 @@ export const themeSet = (theme: string): void => { const themeStyles = create('link') themeStyles.setAttribute('rel', 'stylesheet') - themeStyles.setAttribute('href', `/themes/${theme}/${styleEntry}`) + themeStyles.setAttribute('href', `${ASSET_PATH}themes/${theme}/${styleEntry}`) themeStyles.setAttribute('id', 'themaStyles') const previewDoc = getPreviewDoc() @@ -62,7 +62,7 @@ export const themeSet = (theme: string): void => { const themeScript = previewDoc.createElement('script') themeScript.type = 'text/javascript' - themeScript.src = `/themes/${theme}/index.js` + themeScript.src = `${ASSET_PATH}themes/${theme}/index.js` themeScript.classList.add('themeScript') previewDoc.body.appendChild(themeScript) diff --git a/src/scripts/examples.ts b/src/scripts/examples.ts index 3a12118f1..1deefa505 100644 --- a/src/scripts/examples.ts +++ b/src/scripts/examples.ts @@ -85,29 +85,8 @@ function articleAntibodies(): Promise { ) } -/** - * Transforms implicit relative URLs (`somePath.jpg`) found in the generated example HTML documents, into - * explicitly relative paths ('./somePath.jpg'). This allows the assets to be discovered inside the Theme Editor iframe. - */ -const qualifyRelativeUrls = (): void => { - EXAMPLES.map(example => { - const filePath = `${ex(example.name)}.html` - const contents = fs - .readFileSync(filePath) - .toString() - .replace( - /((?:src|href)=["'])(?!(?:https?)?:\/\/)([^/#.][^'"]+)/gm, - '$1./$2' - ) - - fs.writeFileSync(filePath, contents) - }) -} - // Run each function -Promise.all(EXAMPLES.map(example => example())) - .then(() => qualifyRelativeUrls()) - .catch(err => console.error(err)) +Promise.all(EXAMPLES.map(example => example())).catch(err => console.error(err)) // Generate `../examples/examples.ts` fs.writeFileSync( diff --git a/src/scripts/gallery.ts b/src/scripts/gallery.ts index e1fe7ed16..4cf1aa21a 100644 --- a/src/scripts/gallery.ts +++ b/src/scripts/gallery.ts @@ -67,7 +67,7 @@ async function generateGallery(): Promise { theme, await generateSummary( theme, - `/editor?theme=${theme}`, + `./editor?theme=${theme}`, example as Article ) ] diff --git a/webpack.config.js b/webpack.config.js index fe1a82b41..0c0ff194f 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -10,6 +10,7 @@ const ScriptExtHtmlWebpackPlugin = require('script-ext-html-webpack-plugin') const { DefinePlugin, HotModuleReplacementPlugin } = require('webpack') const contentSource = 'src' +const ASSET_PATH = process.env.ASSET_PATH || '/' // Convert absolute filepaths to project relative ones to use as // output destinations. @@ -32,7 +33,9 @@ module.exports = (env = {}, { mode }) => { const contentBase = isDocs ? 'docs' : 'dist' const entries = [ - './src/**/*.{css,ts,tsx,ttf,woff,woff2}', + './src/**/*.{css,ts,tsx,html,ttf,woff,woff2}', + // Template are used as basis for HtmlWebpackPlugin, and should not be used as an entry points + '!./src/demo/templates/*', // Don’t compile test files for package distribution '!**/*.{d,test}.ts', // These files make use of Node APIs, and do not need to be packaged for Browser targets @@ -43,7 +46,7 @@ module.exports = (env = {}, { mode }) => { // Don’t build HTML demo files for package distribution ...(isDocs || isDevelopment ? ['./src/**/*.{jpg,png,gif}'] - : ['!**/demo/*', '!**/examples/*']) + : ['!**/*.html', '!**/demo/*', '!**/examples/*']) ] const entry = globby.sync(entries).reduce( @@ -63,7 +66,10 @@ module.exports = (env = {}, { mode }) => { new HtmlWebpackPlugin({ filename: 'editor/index.html', template: './src/demo/templates/template.html', - chunks: ['demo/styles', 'themes/stencila/styles', 'demo/app.tsx'] + chunks: ['demo/styles', 'themes/stencila/styles', 'demo/app.tsx'], + templateParameters: { + ASSET_PATH + } }), new HtmlWebpackPlugin({ filename: 'index.html', @@ -80,11 +86,12 @@ module.exports = (env = {}, { mode }) => { return { entry, resolve: { - extensions: ['.ts', '.tsx', '.js', '.css'] + extensions: ['.ts', '.tsx', '.js', '.css', '.html'] }, mode: mode || 'development', output: { path: path.resolve(__dirname, contentBase), + publicPath: ASSET_PATH, filename: '[name].js' }, devServer: { @@ -94,6 +101,7 @@ module.exports = (env = {}, { mode }) => { plugins: [ new CleanWebpackPlugin(), new DefinePlugin({ + 'process.env.ASSET_PATH': JSON.stringify(ASSET_PATH), 'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV), 'process.env.VERSION': JSON.stringify( process.env.VERSION || pkgJson.version @@ -105,12 +113,6 @@ module.exports = (env = {}, { mode }) => { // non TypeScript/JavaScript files (i.e. for font and CSS files). new FileManagerPlugin({ onEnd: { - copy: [ - { - source: 'src/examples/*.{html,html.media}', - destination: `${contentBase}/examples/` - } - ], delete: [ `${contentBase}/**/styles.js`, `${contentBase}/**/styles.js`, @@ -134,6 +136,22 @@ module.exports = (env = {}, { mode }) => { } }, { test: /\.ejs$/, loader: 'ejs-loader' }, + { + test: /\.html$/i, + // Don't transform HtmlWebpackPlugin generated file + exclude: /template\.html$/i, + use: [ + { + loader: 'file-loader', + options: { + name: '[name].[ext]', + outputPath: fileLoaderOutputPath + } + }, + 'extract-loader', + 'html-loader' + ] + }, { test: /\.(css)$/, use: [