diff --git a/.gitignore b/.gitignore index 32756f90..af5dc514 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,4 @@ __benchmarks_results__ # Builds packages/**/build/ packages/**/dist/ +docs/dist/ diff --git a/docs/config.js b/docs/config.js new file mode 100644 index 00000000..1b6255eb --- /dev/null +++ b/docs/config.js @@ -0,0 +1,13 @@ +module.exports = { + // Ordered list of docs what will be loaded + docs: { + pages: [ + 'install', + 'introduction', + ], + }, + blog: { + latest: 'welcome_to_best' + // pages are discovered and ordered automatically + }, +}; diff --git a/docs/content/blog/old_post.md b/docs/content/blog/old_post.md new file mode 100644 index 00000000..a025e309 --- /dev/null +++ b/docs/content/blog/old_post.md @@ -0,0 +1,9 @@ +--- +title: Testing old post +subtitle: Old like hell +created_at: 'May 20, 2019' +--- + +# Behold! + +Yay! \ No newline at end of file diff --git a/docs/content/blog/welcome_to_best.md b/docs/content/blog/welcome_to_best.md new file mode 100644 index 00000000..cf5e9a65 --- /dev/null +++ b/docs/content/blog/welcome_to_best.md @@ -0,0 +1,16 @@ +--- +title: Welcome to Best +subtitle: Its finally here! +author: LWC Team +created_at: May 26, 2019 +--- + +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus vitae nisl et justo mattis fringilla. Quisque vitae mi tellus. Nulla sollicitudin nunc vitae nulla molestie, eu varius mauris vulputate. Suspendisse consequat finibus vehicula. Donec ultricies eros vel pulvinar sagittis. Vestibulum molestie leo ex, ac viverra ligula rhoncus eu. Morbi euismod viverra est vitae aliquam. + +Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Proin ex est, sollicitudin nec tellus ac, ornare molestie sapien. Nulla sed faucibus felis, ut consequat odio. Aenean sollicitudin mauris quis dui hendrerit, at pharetra justo ullamcorper. Quisque eros leo, maximus interdum mauris sed, vulputate egestas est. Praesent interdum fringilla quam, vitae pellentesque ante eleifend quis. Nullam tempor ornare luctus. Nullam vitae blandit dui. Praesent quis dolor ac felis elementum tempus. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Donec eget mauris est. Vivamus rhoncus elit non consequat suscipit. Aliquam laoreet quam lectus, sed sagittis urna scelerisque facilisis. Quisque ac turpis eu odio varius pharetra id ac nunc. Ut lacinia rhoncus ornare. + +## What should we write here? + +Curabitur vestibulum tellus fringilla massa interdum, sed fringilla urna aliquet. Nulla vitae erat id est dapibus mollis vel sed dui. Phasellus sollicitudin lorem eu velit mollis ornare. Quisque vel pellentesque tellus. Mauris nec malesuada odio. Donec ullamcorper metus ligula, eu vestibulum sem efficitur a. Ut et augue vitae mi efficitur tincidunt. Suspendisse sagittis, sem sit amet pretium fermentum, erat leo congue lacus, at volutpat neque massa nec nulla. Morbi ut efficitur nunc. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec scelerisque auctor enim, et accumsan orci fringilla sed. Nam rhoncus blandit nisi in euismod. Ut cursus imperdiet tortor, euismod porttitor ex lobortis pretium. + +Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. In sollicitudin augue ut ante dictum, id rutrum erat euismod. In ut dui odio. Aenean dictum nunc in arcu tincidunt, a tempus turpis eleifend. Cras leo sem, bibendum posuere risus id, pulvinar malesuada ante. Phasellus posuere sem odio, eget faucibus arcu dignissim quis. Vivamus at lectus ac odio ultrices venenatis vitae sed leo. Etiam nec lorem sed odio finibus fringilla ac vel felis. Pellentesque commodo ligula ac diam vestibulum vehicula. diff --git a/docs/content/docs/install.md b/docs/content/docs/install.md new file mode 100644 index 00000000..a2978cdd --- /dev/null +++ b/docs/content/docs/install.md @@ -0,0 +1,111 @@ +--- +title: Installation +--- + +# Installation + +::: tip +If you aren't familiar with build tools but want to explore Best... +::: + +## Best CLI + +Foo Bar + +```bash +npx lwc-create-app my-app +cd my-app +npm run watch +``` + +To install the CLI, you must have [Node.js](https://nodejs.org/) installed, with at least npm 5.2+. You should be familiar with either [npm](https://www.npmjs.com/) or [yarn](https://yarnpkg.com/). The npx tool is a package runner that installs with npm 5.2+. + +For information about component naming and bundle structure, see [Component Bundles](reference#component-bundles). + +## Tools + +To develop Lightning web components, you can use just about any code editor and tools. + +For code formatting, we recommend [Prettier](https://Prettier.io/). Prettier supports HTML, CSS, and Javascript, which are the files you write to create Lightning web components. + +To install and use Prettier, see the official [documentation](https://Prettier.io/docs/en/install.html). If you're using Git, it's a good idea to use a [pre-commit hook](https://Prettier.io/docs/en/precommit.html) to ensure that code is formatted before it's committed to source control. + +To configure Prettier, add a [configuration file](https://Prettier.io/docs/en/configuration.html) to your project. To correctly format HTML templates with Prettier, set the `parser` to `lwc`. The parser is just HTML, but it tells Prettier not to add quotes around template properties in HTML attributes as required by LWC. + +The following example sets all HTML files to use the `lwc` parser. + +```json +{ + "overrides": [ + { + "files": "*.html", + "options": { "parser": "lwc" } + } + ] +} +``` + +## Recipes + +The [`github.com/trailheadapps/lwc-recipes-oss`](https://github.com/trailheadapps/lwc-recipes-oss) repo includes simple code recipes that teach you how to build apps. The recipes are used as code examples throughout this developer guide. + +```bash +git clone https://github.com/trailheadapps/lwc-recipes-oss.git +cd lwc-recipes-oss +``` + +You can view some of the recipes in the Lightning Web Components recipes app: +[recipes.lwc.dev](https://recipes.lwc.dev). + +## Playground + +The simplest recipe is the `helloWorld` component. The `name` property in the component's JavaScript class binds to the component's HTML template. Change `World` to `Earth` to see the binding in action. + +Add another property in `helloWorld.js`. + +```js +@api greeting = 'Welcome to Lightning Web Components!' +``` + +Don't forget to add `{greeting}` in the `helloWorld.html` template. + +The `@api` decorator makes the `name` property public. Because the `name` and `greeting` properties are public, a component that consumes the `helloWorld` component can set their values. + +If we remove `@api`, the property still binds to the HTML template but it's private. To see for yourself, remove `@api`. + +To learn more, see [HTML Templates](html_templates). + +## Supported Browsers + +| Browser | Version | +| --- | --- | +|Microsoft® Internet Explorer® | IE 11* | +|Microsoft® Edge| Latest | +|Google Chrome™|Latest | +|Mozilla® Firefox®| Latest| +|Apple® Safari®| 12.x+| + +::: note +For IE 11, Lightning Web Components uses compatibility mode. Code is transpiled down to ES5 and the required polyfills are added. Components work in compatibility mode, but performance suffers. To develop Lightning web components that run in IE 11, follow the [Compat Performance](https://github.com/salesforce/eslint-plugin-lwc#compat-performance) rules in the ESLint Plugin for Lightning Web Components Github repo. +::: + +## Supported JavaScript + +To develop Lightning web components, use the latest versions of JavaScript. + +Lightning Web Components JavaScript support includes: + +- ES6 \(ECMAScript 2015\) +- ES7 \(ECMAScript 2016\) +- ES8 \(ECMAScript 2017\)—excluding Shared Memory and Atomics +- ES9 \(ECMAScript 2018\)—including only [Object Spread Properties](https://github.com/tc39/proposal-object-rest-spread) \(not Object Rest Properties\) + +A huge benefit of Lightning Web Components is that you write standard JavaScript code. The Salesforce engineers who developed Lightning Web Components are contributing members of the Ecma International Technical Committee 39 \([TC39](https://tc39.github.io/ecma262/)\), which is the committee that evolves JavaScript. Salesforce is also a member of the [World Wide Web Consortium \(W3C\)](https://www.w3.org/Consortium/Member/List). + +This developer guide explains how to develop Lightning web components and documents the [directives](reference#html-template-directives), [decorators](reference#javascript-decorators), and [lifecycle hooks](lifecycle) that are unique to the programming model. + +This developer guide doesn’t document standard JavaScript or teach JavaScript fundamentals. Standard JavaScript is documented in the [Mozilla Developer Network \(MDN\) JavaScript Reference](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference). If you’re looking for documentation for a function, try MDN first. For example, if you’re looking for information about `addEventListener()`, use MDN. + +::: tip +To learn JavaScript \(or if you want a refresher\), start with the [Modern JavaScript Development](https://trailhead.salesforce.com/en/content/learn/modules/modern-javascript-development?trail_id=learn-to-work-with-javascript) Trailhead module. In just an hour and fifteen minutes, you’ll be up-to-date and ready to develop Lightning web components. +::: diff --git a/docs/content/docs/introduction.md b/docs/content/docs/introduction.md new file mode 100644 index 00000000..5fe52f8b --- /dev/null +++ b/docs/content/docs/introduction.md @@ -0,0 +1,11 @@ +--- +title: Dev Guide +--- + +# Dev Guide + +Best is amazing! + +::: tip +The name of Best, is because... blah. +::: diff --git a/docs/package.json b/docs/package.json new file mode 100644 index 00000000..6858ebd7 --- /dev/null +++ b/docs/package.json @@ -0,0 +1,51 @@ +{ + "name":"@best/docs", + "version": "4.0.0", + "private": true, + "scripts": { + "test:links": "node scripts/verify-doc-links", + "clean": "rm -rf dist/", + "build:assets": "node scripts/generate-assets", + "build:home": "node scripts/generate-homepage.js", + "build:notfound": "node scripts/generate-notfound.js", + "build:docs": "node scripts/generate-documentation.js", + "build:blog": "node scripts/generate-blog.js", + "build": "yarn build:assets && yarn build:home && yarn build:blog && yarn build:docs && yarn build:notfound", + "watch": "cross-env WATCH=1 yarn start", + "start": "node ./src/server/index.js" + }, + "dependencies": { + "compression": "~1.7.3", + "express": "~4.16.4", + "express-winston": "~3.0.1", + "serve-static": "~1.14.1", + "winston": "~3.1.0" + }, + "devDependencies": { + "@lwc/compiler": "~1.0.0", + "@lwc/engine": "~1.0.0", + "@lwc/jest-preset": "~1.0.0", + "@lwc/rollup-plugin": "~1.0.0", + "@lwc/synthetic-shadow": "~1.0.0", + "@lwc/wire-service": "~1.0.0", + "cpy": "~7.0.1", + "dateformat": "^3.0.3", + "decamelize": "^3.2.0", + "escape-html": "1.0.3", + "gray-matter": "~4.0.2", + "hash-sum": "^1.0.2", + "lint-staged": "~8.1.0", + "lru-cache": "^5.1.1", + "markdown-it": "~8.4.1", + "markdown-it-anchor": "~5.0.2", + "markdown-it-chain": "~1.3.0", + "markdown-it-container": "2.0.0", + "markdown-it-emoji": "~1.4.0", + "markdown-link-check": "^3.7.3", + "mkdirp": "~0.5.1", + "prismjs": "~1.16.0", + "reload": "~3.0.1", + "watch": "^1.0.2", + "cross-env": "^5.2.0" + } +} diff --git a/docs/scripts/build-assets.js b/docs/scripts/build-assets.js new file mode 100644 index 00000000..58b13b6f --- /dev/null +++ b/docs/scripts/build-assets.js @@ -0,0 +1,50 @@ +const fs = require('fs'); +const path = require('path'); +const cpy = require('cpy'); +const mkdirp = require('mkdirp'); +const crypto = require('crypto'); + +const { + SRC_DIR, + DIST_DIR, + LWC_ENGINE_PATH, + PAGE_STYLESHEETS, + PAGE_STYLESHEETS_PROD_DIR, + LWC_VERSION, +} = require('./config'); +const ENGINE_FILE = `engine_v${LWC_VERSION}.js`; + +function buildAndCompileStyles(dist) { + const bundleSrc = PAGE_STYLESHEETS.reduce((str, stylesheetPath) => { + const abs = path.join(dist, stylesheetPath); + return (str += fs.readFileSync(abs, 'utf-8')); + }, ''); + + const hash = crypto + .createHash('md5') + .update(bundleSrc) + .digest('hex'); + mkdirp.sync(PAGE_STYLESHEETS_PROD_DIR); + fs.writeFileSync( + path.join(PAGE_STYLESHEETS_PROD_DIR, `bundle.${hash}.css`), + bundleSrc, + 'utf-8', + ); +} + +module.exports = async function() { + // Copy all assets + await cpy(['**', '!images/favicon.ico'], path.join(DIST_DIR, 'assets'), { + cwd: path.resolve(SRC_DIR, 'assets'), + parents: true, + }); + + // Copy the `favicon.ico` in the root + await cpy(path.resolve(SRC_DIR, 'assets/images/favicon.ico'), DIST_DIR); + + // Compile styles + await buildAndCompileStyles(DIST_DIR); + + // Copy engine + fs.copyFileSync(LWC_ENGINE_PATH, path.join(DIST_DIR, `assets/js/lwc/${ENGINE_FILE}`)); +}; diff --git a/docs/scripts/build-blog.js b/docs/scripts/build-blog.js new file mode 100644 index 00000000..b4d2f146 --- /dev/null +++ b/docs/scripts/build-blog.js @@ -0,0 +1,67 @@ +// -- modules --------------------------------------------------------------------------- +const path = require('path'); +const fs = require('fs'); +const { readHtml } = require('./utils/readFile'); +const parseDocument = require('./utils/parseDocument'); +const parseSidebar = require('./utils/parseSidebar'); +const buildPage = require('./utils/buildDocPage'); +const buildBlogPostHeader = require('./utils/buildBlogPostHeader'); +const markdown = require('./utils/markdown'); + +// -- Global Config --------------------------------------------------------------------- +const { SRC_DIR, DIST_DIR, BLOG_DIR } = require('./config'); +const HTML_TEMPLATE = readHtml('template', SRC_DIR); +const MD_INSTANCE = markdown(); + +// -- Helpers --------------------------------------------------------------------------- +async function generatePageHtml(pageDoc, sidebarData, template, opts) { + const { docName } = pageDoc; + const htmlContent = await buildPage(pageDoc, sidebarData, template, opts); + fs.writeFileSync(path.resolve(DIST_DIR, `${docName}.html`), htmlContent, 'utf-8'); +} + +function createDate(str) { + const date = Date.parse(str); + if (isNaN(date)) { + throw new Error('Invalid date for blog entry'); + } + + return new Date(date); +} + +function sortBlogPages(a, b) { + const timeA = createDate(a.metadata.created_at); + const timeB = createDate(b.metadata.created_at); + return timeA.getTime() < timeB.getTime(); +} + +function beforeRender(markdown, metadata) { + return buildBlogPostHeader(metadata) + markdown; +} + +async function generatePageHtml(pageDoc, sidebarData, template, opts) { + const { docName } = pageDoc; + const htmlContent = await buildPage(pageDoc, sidebarData, template, opts); + fs.writeFileSync(path.resolve(DIST_DIR, `blog_${docName}.html`), htmlContent, 'utf-8'); +} + +// -- API ------------------------------------------------------------------------------- +module.exports = async function buildDocumentation() { + // For every markdown document file generate a + // page representation that holds all the metadata + const BLOG_LIST = fs.readdirSync(BLOG_DIR).map(file => path.basename(file, '.md')); + const pageDocList = BLOG_LIST.map(doc => + parseDocument(doc, BLOG_DIR, MD_INSTANCE, { beforeRender }), + ).sort(sortBlogPages); + + // We will process each page independently + for (const pageDocument of pageDocList) { + const pageSidebar = parseSidebar(pageDocument, pageDocList, { levels: 1 }); + // Generate the HTML for a given page, sidebar and template + await generatePageHtml(pageDocument, pageSidebar, HTML_TEMPLATE, { + pageClasses: 'blog content-wrapper flex-wrapper', + activeTab: 'blog', + prefixUrl: '/blog', + }); + } +}; diff --git a/docs/scripts/build-documentation.js b/docs/scripts/build-documentation.js new file mode 100644 index 00000000..625a50f5 --- /dev/null +++ b/docs/scripts/build-documentation.js @@ -0,0 +1,37 @@ +// -- modules --------------------------------------------------------------------------- +const path = require('path'); +const fs = require('fs'); +const { readHtml } = require('./utils/readFile'); +const parseDocument = require('./utils/parseDocument'); +const parseSidebar = require('./utils/parseSidebar'); +const buildPage = require('./utils/buildDocPage'); +const markdown = require('./utils/markdown'); + +// -- Global Config --------------------------------------------------------------------- +const { DOCS_LIST, SRC_DIR, DIST_DIR, DOCS_DIR } = require('./config'); +const HTML_TEMPLATE = readHtml('template', SRC_DIR); +const MD_INSTANCE = markdown(); + +// -- Helpers --------------------------------------------------------------------------- +async function generatePageHtml(pageDoc, sidebarData, template, opts) { + const { docName } = pageDoc; + const htmlContent = await buildPage(pageDoc, sidebarData, template, opts); + fs.writeFileSync(path.resolve(DIST_DIR, `${docName}.html`), htmlContent, 'utf-8'); +} + +// -- API ------------------------------------------------------------------------------- +module.exports = async function buildDocumentation() { + // For every markdown document file generate a + // page representation that holds all the metadata + const pageDocList = DOCS_LIST.map(doc => parseDocument(doc, DOCS_DIR, MD_INSTANCE)); + + // We will process each page independently + for (const pageDocument of pageDocList) { + const pageSidebar = parseSidebar(pageDocument, pageDocList); + // Generate the HTML for a given page, sidebar and template + await generatePageHtml(pageDocument, pageSidebar, HTML_TEMPLATE, { + activeTab: 'guide', + prefixUrl: '/guide', + }); + } +}; diff --git a/docs/scripts/build-homepage.js b/docs/scripts/build-homepage.js new file mode 100644 index 00000000..ff9425eb --- /dev/null +++ b/docs/scripts/build-homepage.js @@ -0,0 +1,36 @@ +// -- modules --------------------------------------------------------------------------- +const path = require('path'); +const fs = require('fs'); +const { readHtml } = require('./utils/readFile'); +const { __PROD__ } = require('./config'); +const buildPageHtml = require('./utils/buildPageHtml'); +const buildPageStyles = require('./utils/buildPageStyles'); +const buildNavBar = require('./utils/buildNavbar'); + +// -- Global Config --------------------------------------------------------------------- +const { SRC_DIR, DIST_DIR } = require('./config'); +const EMPTY_STRING = ''; +const TITLE = 'BestJS'; + +// -- Helpers --------------------------------------------------------------------------- +function buildHomePage(template) { + return buildPageHtml( + template, + { + title: TITLE, + navBar: buildNavBar(), + pageClasses: 'home', + sideBar: EMPTY_STRING, + body: readHtml('home', SRC_DIR), + headerStyles: buildPageStyles(), + headerScripts: EMPTY_STRING, + }, + { prod: __PROD__ }, + ); +} + +// -- API ------------------------------------------------------------------------------- +module.exports = function writeHomePage() { + const htmlContent = buildHomePage(readHtml('template', SRC_DIR)); + fs.writeFileSync(path.resolve(DIST_DIR, 'home.html'), htmlContent, 'utf-8'); +}; diff --git a/docs/scripts/build-notfound.js b/docs/scripts/build-notfound.js new file mode 100644 index 00000000..6aa05495 --- /dev/null +++ b/docs/scripts/build-notfound.js @@ -0,0 +1,36 @@ +// -- modules --------------------------------------------------------------------------- +const path = require('path'); +const fs = require('fs'); +const { readHtml } = require('./utils/readFile'); +const { __PROD__ } = require('./config'); +const buildPageHtml = require('./utils/buildPageHtml'); +const buildPageStyles = require('./utils/buildPageStyles'); +const buildNavBar = require('./utils/buildNavbar'); + +// -- Global Config --------------------------------------------------------------------- +const { SRC_DIR, DIST_DIR } = require('./config'); +const EMPTY_STRING = ''; +const TITLE = 'Page Not Found'; + +// -- Helpers --------------------------------------------------------------------------- +function buildNotFoundPage(template) { + return buildPageHtml( + template, + { + title: TITLE, + navBar: buildNavBar(), + pageClasses: 'home notfound', + sideBar: EMPTY_STRING, + body: readHtml('notfound', SRC_DIR), + headerStyles: buildPageStyles(), + headerScripts: EMPTY_STRING, + }, + { prod: __PROD__ }, + ); +} + +// -- API ------------------------------------------------------------------------------- +module.exports = function writeNotFoundPage() { + const htmlContent = buildNotFoundPage(readHtml('template', SRC_DIR)); + fs.writeFileSync(path.resolve(DIST_DIR, 'notfound.html'), htmlContent, 'utf-8'); +}; diff --git a/docs/scripts/config.js b/docs/scripts/config.js new file mode 100644 index 00000000..9c77cc90 --- /dev/null +++ b/docs/scripts/config.js @@ -0,0 +1,70 @@ +const fs = require('fs'); +const path = require('path'); +const SITE_CONFIG = require('../config'); + +const SRC_DIR = path.resolve(__dirname, '../src/client'); +const DIST_DIR = path.resolve(__dirname, '../dist'); +const DOCS_DIR = path.resolve(__dirname, '../content/docs'); +const TUTORIAL_DIR = path.resolve(__dirname, '../content/tutorial'); +const COMMUNITY_DIR = path.resolve(__dirname, '../content/community'); +const BLOG_DIR = path.resolve(__dirname, '../content/blog'); +const PAGE_STYLESHEETS_PROD_DIR = path.join(DIST_DIR, '/assets/css/prod'); + +const PAGE_STYLESHEETS = [ + '/assets/css/normalize.css', + '/assets/css/main.css', + '/assets/css/docs.css', + '/assets/css/blog.css', + '/assets/css/prismjs/themes/prism.css', +]; + +const __ENV__ = process.env.NODE_ENV || 'development'; +const __PROD__ = __ENV__ === 'production'; + +const LWC_COMPILER_CONFIG = { + exclude: ['**/codeMirror/**'], + resolveFromPackages: false, + stylesheetConfig: { + customProperties: { + allowDefinition: true, + }, + }, +}; + +const LWC_VERSION = '100'; +const LWC_ENGINE_PATH = require.resolve('@lwc/engine/dist/umd/es2017/engine'); +const DOCS_LIST = SITE_CONFIG.docs.pages; + +function getStyleSheets() { + if (__PROD__) { + if (fs.existsSync(PAGE_STYLESHEETS_PROD_DIR)) { + return fs + .readdirSync(PAGE_STYLESHEETS_PROD_DIR) + .map(f => path.join('/assets/css/prod', f)); + } else { + return []; + } + } else { + return PAGE_STYLESHEETS; + } +} + +module.exports = { + SRC_DIR, + DIST_DIR, + DOCS_DIR, + BLOG_DIR, + TUTORIAL_DIR, + COMMUNITY_DIR, + + __ENV__, + __PROD__, + + LWC_COMPILER_CONFIG, + LWC_ENGINE_PATH, + LWC_VERSION, + DOCS_LIST, + PAGE_STYLESHEETS, + PAGE_STYLESHEETS_PROD_DIR, + getStyleSheets, +}; diff --git a/docs/scripts/generate-assets.js b/docs/scripts/generate-assets.js new file mode 100644 index 00000000..d1794bd3 --- /dev/null +++ b/docs/scripts/generate-assets.js @@ -0,0 +1,7 @@ +/* eslint-disable no-console */ +const buildAssets = require('./build-assets'); + +buildAssets().catch(err => { + console.log(err); + throw err; +}); diff --git a/docs/scripts/generate-blog.js b/docs/scripts/generate-blog.js new file mode 100644 index 00000000..1b020e33 --- /dev/null +++ b/docs/scripts/generate-blog.js @@ -0,0 +1,7 @@ +/* eslint-disable no-console */ +const buildBlog = require('./build-blog'); + +buildBlog().catch(err => { + console.log(err); + throw err; +}); diff --git a/docs/scripts/generate-documentation.js b/docs/scripts/generate-documentation.js new file mode 100644 index 00000000..e44f3a9f --- /dev/null +++ b/docs/scripts/generate-documentation.js @@ -0,0 +1,7 @@ +/* eslint-disable no-console */ +const buildDocumentation = require('./build-documentation'); + +buildDocumentation().catch(err => { + console.log(err); + throw err; +}); diff --git a/docs/scripts/generate-homepage.js b/docs/scripts/generate-homepage.js new file mode 100644 index 00000000..8eaabd97 --- /dev/null +++ b/docs/scripts/generate-homepage.js @@ -0,0 +1,2 @@ +const buildHomePage = require('./build-homepage'); +buildHomePage(); diff --git a/docs/scripts/generate-notfound.js b/docs/scripts/generate-notfound.js new file mode 100644 index 00000000..44e55675 --- /dev/null +++ b/docs/scripts/generate-notfound.js @@ -0,0 +1,2 @@ +const buildNotFound = require('./build-notfound'); +buildNotFound(); diff --git a/docs/scripts/utils/buildBlogPostHeader.js b/docs/scripts/utils/buildBlogPostHeader.js new file mode 100644 index 00000000..11961a6f --- /dev/null +++ b/docs/scripts/utils/buildBlogPostHeader.js @@ -0,0 +1,13 @@ +const dateformat = require('dateformat'); +module.exports = function buildBlogPostHeader({ title, subtitle, created_at, author, twitter }) { + const sub = subtitle ? `

${subtitle}

` : ''; + return `
+

${title}

+ ${sub} +

+ ${author} + +

+
+`; +}; diff --git a/docs/scripts/utils/buildDocPage.js b/docs/scripts/utils/buildDocPage.js new file mode 100644 index 00000000..b08e36b5 --- /dev/null +++ b/docs/scripts/utils/buildDocPage.js @@ -0,0 +1,40 @@ +const path = require('path'); +const buildSidebar = require('./buildSidebar'); +const buildWebComponents = require('./buildPageComponents'); +const buildPageScripts = require('./buildPageScripts'); +const buildPageStyles = require('./buildPageStyles'); +const buildNavBar = require('./buildNavbar'); +const buildFooter = require('./buildFooter'); +const buildPageHtml = require('./buildPageHtml'); +const { __PROD__, SRC_DIR } = require('../config'); +const modulesDir = path.resolve(SRC_DIR, 'modules'); + +function buildHtml(pageDoc, sidebarData, opts) { + const { html } = pageDoc; + const index = sidebarData.findIndex(d => d.id === pageDoc.docName); + const prev = sidebarData[index - 1]; + const next = sidebarData[index + 1]; + + return html + buildFooter(prev, next, opts); +} + +module.exports = async function buildDocPage(pageDoc, sidebarData, template, opts = {}) { + const { + metadata: { title }, + } = pageDoc; + const pageScripts = await buildWebComponents(pageDoc, { modulesDir }); + + return buildPageHtml( + template, + { + title, + pageClasses: opts.pageClasses || 'content-wrapper flex-wrapper', + navBar: buildNavBar(opts), + sideBar: buildSidebar(sidebarData, opts), + body: buildHtml(pageDoc, sidebarData, opts), + headerStyles: buildPageStyles(), + headerScripts: buildPageScripts(pageScripts), + }, + { prod: __PROD__ }, + ); +}; diff --git a/docs/scripts/utils/buildFooter.js b/docs/scripts/utils/buildFooter.js new file mode 100644 index 00000000..9ad1462c --- /dev/null +++ b/docs/scripts/utils/buildFooter.js @@ -0,0 +1,15 @@ +module.exports = function buildFooter(prev, next, { prefixUrl = '' } = {}) { + const prevHTML = prev + ? `${prev.title}` + : ''; + const nextHTML = next + ? `${next.title}` + : ''; + + return ` +
+ + `; +}; diff --git a/docs/scripts/utils/buildNavbar.js b/docs/scripts/utils/buildNavbar.js new file mode 100644 index 00000000..843762f8 --- /dev/null +++ b/docs/scripts/utils/buildNavbar.js @@ -0,0 +1,20 @@ +const DEFAULT_NAVBAR = [ + { + id: 'guide', + href: '/guide/introduction', + text: 'Guide', + }, + { + id: 'blog', + href: '/blog', + text: 'Blog', + } +]; + +module.exports = function buildNavBar({ activeTab } = {}) { + return DEFAULT_NAVBAR.map(tab => { + const { id, href, text } = tab; + const active = id === activeTab; + return `
  • ${text}
  • `; + }).join('\n'); +}; diff --git a/docs/scripts/utils/buildPageComponents.js b/docs/scripts/utils/buildPageComponents.js new file mode 100644 index 00000000..80cc52f3 --- /dev/null +++ b/docs/scripts/utils/buildPageComponents.js @@ -0,0 +1,69 @@ +const path = require('path'); +const { rollup } = require('rollup'); +const lwcPlugin = require('@lwc/rollup-plugin'); +const kebabToCamelCase = require('./utilKebabToCamelCase'); +const { DIST_DIR, LWC_COMPILER_CONFIG } = require('../config'); +const PAGE_CMP_PREFIX = 'page-docs'; + +function generateWebComponentRegistration(customElementName) { + const moduleName = kebabToCamelCase(customElementName); + const Ctor = moduleName.replace('/', '$'); + return [ + `import ${Ctor} from "${moduleName}";`, + `customElements.define("${customElementName}", buildCustomElementConstructor(${Ctor}));`, + '', + ].join('\n'); +} + +function syntheticPagePlugin({ pageDoc }) { + const { components } = pageDoc; + const webComponentBoot = components.map(generateWebComponentRegistration).join(''); + + return { + resolveId(id) { + if (id.startsWith(PAGE_CMP_PREFIX)) { + return id; + } + }, + load(id) { + if (id.startsWith(PAGE_CMP_PREFIX)) { + return ` + import { buildCustomElementConstructor } from "lwc"; + ${webComponentBoot} + `; + } + }, + }; +} + +module.exports = async function buildWebComponents(pageDoc, { modulesDir }) { + const { docName, components } = pageDoc; + if (!components || components.length === 0) { + return []; + } + + const rollupBundler = await rollup({ + input: `${PAGE_CMP_PREFIX}-${docName}.js`, + external: ['lwc', '/assets/js/lwc/compiler.js'], + plugins: [ + syntheticPagePlugin({ pageDoc }), + lwcPlugin({ + ...LWC_COMPILER_CONFIG, + rootDir: modulesDir, + }), + ], + chunkGroupingSize: 20, + experimentalOptimizeChunks: true, + }); + + const results = await rollupBundler.write({ + dir: path.resolve(DIST_DIR, 'assets/js'), + format: 'iife', + globals: { lwc: 'Engine' }, + entryFileNames: '[name]-[hash].js', + chunkFileNames: '[name]-[hash].js', + sourcemap: false, + }); + + return results.output.map(r => r.fileName); +}; diff --git a/docs/scripts/utils/buildPageHtml.js b/docs/scripts/utils/buildPageHtml.js new file mode 100644 index 00000000..63ccbeab --- /dev/null +++ b/docs/scripts/utils/buildPageHtml.js @@ -0,0 +1,24 @@ +const TMPL = { + TITLE: '{{TITLE}}', + PAGE_CLASSES: '{{PAGE_CLASSES}}', + NAVBAR: '{{NAVBAR}}', + SIDEBAR: '{{SIDEBAR}}', + BODY: '{{BODY}}', + HEADER_STYLES: '{{HEADER_STYLES}}', + HEADER_SCRIPTS: '{{HEADER_SCRIPTS}}', + BOTTOM_PH: '{{BOTTOM_RUNTIME_PLACEHOLDER}}', +}; + +module.exports = function buildHtmlTemplate(template, options, { prod }) { + const { title, navBar, sideBar, body, headerStyles, headerScripts, pageClasses } = options; + + return template + .replace(TMPL.TITLE, title) + .replace(TMPL.PAGE_CLASSES, pageClasses) + .replace(TMPL.NAVBAR, navBar) + .replace(TMPL.SIDEBAR, sideBar) + .replace(TMPL.BODY, body) + .replace(TMPL.HEADER_STYLES, headerStyles) + .replace(TMPL.HEADER_SCRIPTS, headerScripts) + .replace(TMPL.BOTTOM_PH, prod ? '' : TMPL.BOTTOM_PH); +}; diff --git a/docs/scripts/utils/buildPageScripts.js b/docs/scripts/utils/buildPageScripts.js new file mode 100644 index 00000000..8ab96033 --- /dev/null +++ b/docs/scripts/utils/buildPageScripts.js @@ -0,0 +1,8 @@ +const { LWC_VERSION } = require('../config'); +const DEFAULT_SCRIPTS = [`lwc/engine_v${LWC_VERSION}.js`]; + +module.exports = function buildPageScripts(pageScripts) { + const scripts = DEFAULT_SCRIPTS.concat(pageScripts); + const list = scripts.map(src => ``); + return list.join('\n'); +}; diff --git a/docs/scripts/utils/buildPageStyles.js b/docs/scripts/utils/buildPageStyles.js new file mode 100644 index 00000000..6227d517 --- /dev/null +++ b/docs/scripts/utils/buildPageStyles.js @@ -0,0 +1,9 @@ +const path = require('path'); +const { getStyleSheets } = require('../config'); + +module.exports = function buildPageStyles(pageStyles = []) { + const defaultStyles = getStyleSheets(); + const styles = defaultStyles.concat(pageStyles); + const list = styles.map(src => ``); + return list.join('\n'); +}; diff --git a/docs/scripts/utils/buildSidebar.js b/docs/scripts/utils/buildSidebar.js new file mode 100644 index 00000000..4d5d87c9 --- /dev/null +++ b/docs/scripts/utils/buildSidebar.js @@ -0,0 +1,76 @@ +/* +This is an example of expected markup: + +