diff --git a/.cloudbuild/staging-check.js b/.cloudbuild/staging-check.js deleted file mode 100644 index 8025827f..00000000 --- a/.cloudbuild/staging-check.js +++ /dev/null @@ -1,79 +0,0 @@ -#!/usr/bin/env node -/** - * Copyright 2020 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @fileoverview This file compares the current commit hash against the staging site. If they're - * different, and no other build tasks are running, we kick off "staging-deploy". - */ - -const { ErrorReporting } = require('@google-cloud/error-reporting'); -const { CloudBuildClient } = require('@google-cloud/cloudbuild'); -const { getDeployedVersion, getCurrentVersion } = require('../build/git-version.js'); - -const client = new CloudBuildClient(); -const errors = new ErrorReporting(); - -// This is the trigger ID of "staging-deploy" for "santa-staging". -const deployTriggerId = 'd6401587-de8b-4507-ae71-bc516fdfc64a'; - -(async () => { - const deployedVersion = await getDeployedVersion(); - const currentVersion = await getCurrentVersion(); - console.log(`version deployed="${deployedVersion}" local="${currentVersion}""`); - - if (deployedVersion && deployedVersion === currentVersion) { - console.log( - 'The current and deployed versions are the same, not continuing build.' - ); - return; - } - - console.log( - 'The current and deployed versions are different, kicking off deploy build.' - ); - - // Check if there are any existing builds. - const ret = client.listBuildsAsync({ - projectId: process.env.PROJECT_ID, - pageSize: 1, - filter: `trigger_id="${deployTriggerId}" AND (status="WORKING" OR status="QUEUED")`, - }); - - // This is an async iterable, check if we have at least one, if so, there's an active build. - let activeBuild = false; - for await (const _build of ret) { - activeBuild = true; - break; - } - if (activeBuild) { - console.log( - 'There is a current active or queued build. Not starting another.' - ); - return; - } - - try { - // This just waits for the build to be kicked off, not for its completion (it - // returns a LROperation). - await client.runBuildTrigger({ - projectId: process.env.PROJECT_ID, - triggerId: deployTriggerId, - }); - } catch (e) { - errors.report(e); - } -})(); diff --git a/.cloudbuild/staging-check.yaml b/.cloudbuild/staging-check.yaml deleted file mode 100644 index f5b403a9..00000000 --- a/.cloudbuild/staging-check.yaml +++ /dev/null @@ -1,16 +0,0 @@ -# This Cloud Build task can kick off the staging deploy if the hash has changed. - -steps: - - name: node - id: 'Install dependencies' - entrypoint: npm - args: ['ci'] - - - name: node - id: 'Verify and maybe kick off build for new version' - entrypoint: npm - args: ['run', 'staging-check'] - -options: - env: - - 'PROJECT_ID=$PROJECT_ID' diff --git a/.cloudbuild/staging-deploy.sh b/.cloudbuild/staging-deploy.sh deleted file mode 100644 index f6f06658..00000000 --- a/.cloudbuild/staging-deploy.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/bash - -set -eu - -BASEURL="https://santa-staging.firebaseapp.com/" - -# move to the root directory of santa-tracker-web -ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -STAGING_ROOT="$ROOT/staging" -cd $ROOT/.. - -# build! -node ./release.js --baseurl=$BASEURL --minify=false - -# move prod to GaE skeleton -rm -rf $STAGING_ROOT/appengine/prod -mv dist/prod $STAGING_ROOT/appengine/prod - -# move static to firebase -# Note that when we deploy, we clobber the "old" deploy. People mucking with the staging site will -# be suddenly surprised when they can't load content! -mkdir -p $STAGING_ROOT/firebase/public -mv dist/_static/* $STAGING_ROOT/firebase/public diff --git a/.cloudbuild/staging-deploy.yaml b/.cloudbuild/staging-deploy.yaml deleted file mode 100644 index 66109b49..00000000 --- a/.cloudbuild/staging-deploy.yaml +++ /dev/null @@ -1,36 +0,0 @@ -# This Cloud Build task runs the deploy to staging. - -steps: - - name: node - id: 'Install dependencies' - entrypoint: npm - args: ['ci'] - - # This is an optional dependency of "google-closure-compiler", but it doesn't always install on - # Cloud Build for some reason. We need this as we can't use the Java compiler in the Node image. - - name: node - id: 'Force install Closure native Linux binary' - entrypoint: 'npm' - args: ['install', 'google-closure-compiler-linux'] - - - name: node - id: 'Build' - entrypoint: bash - args: ['.cloudbuild/staging-deploy.sh'] - - - name: 'gcr.io/$PROJECT_ID/firebase' - dir: '.cloudbuild/staging/firebase' - args: ['deploy', '--only', 'hosting', '--project', 'santa-staging'] - - - name: 'gcr.io/cloud-builders/gcloud' - dir: '.cloudbuild/staging/appengine' - entrypoint: 'bash' - args: ['-c', 'gcloud app deploy --version hohoho --project santa-staging'] - -options: - machineType: 'E2_HIGHCPU_32' # yolo - env: - - 'PROJECT_ID=$PROJECT_ID' - - 'NODE_OPTIONS="--max-old-space-size=32768"' - -timeout: 1800s diff --git a/.cloudbuild/staging/.gitignore b/.cloudbuild/staging/.gitignore deleted file mode 100644 index 15908608..00000000 --- a/.cloudbuild/staging/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -appengine/prod -firebase/public diff --git a/.cloudbuild/staging/appengine/app.yaml b/.cloudbuild/staging/appengine/app.yaml deleted file mode 100644 index 3e5f0988..00000000 --- a/.cloudbuild/staging/appengine/app.yaml +++ /dev/null @@ -1,6 +0,0 @@ -runtime: go115 - -handlers: -- url: /.* - script: auto - secure: always diff --git a/.cloudbuild/staging/appengine/go.mod b/.cloudbuild/staging/appengine/go.mod deleted file mode 100644 index 166c6c5b..00000000 --- a/.cloudbuild/staging/appengine/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module github.com/google/santa-tracker-web - -go 1.15 diff --git a/.cloudbuild/staging/appengine/go.sum b/.cloudbuild/staging/appengine/go.sum deleted file mode 100644 index e69de29b..00000000 diff --git a/.cloudbuild/staging/appengine/http.go b/.cloudbuild/staging/appengine/http.go deleted file mode 100644 index 5f50a4c1..00000000 --- a/.cloudbuild/staging/appengine/http.go +++ /dev/null @@ -1,53 +0,0 @@ -package main - -import ( - "fmt" - "log" - "net/http" - "os" - "regexp" -) - -var ( - intlMatcher = regexp.MustCompile(`^/intl/([-_\w]+)(/.*|)$`) -) - -func main() { - fileServer := http.FileServer(http.Dir("prod")) - handler := rewriteLang(fileServer) - - http.Handle("/", handler) - - port := os.Getenv("PORT") - if port == "" { - port = "8080" - } - addr := ":" + port - log.Printf("Serving on %s...", addr) - http.ListenAndServe(addr, nil) -} - -// pathForLangFile serves the specified "rest" file for the given lang, falling -// back to the top-level if it doesn't exist there. -func pathForLangFile(lang, rest string) string { - if len(rest) != 0 && rest[0] == '/' { - rest = rest[1:] // trim leading slash - } - check := fmt.Sprintf("prod/intl/%s_ALL/%s", lang, rest) - if _, err := os.Stat(check); err != nil && os.IsNotExist(err) { - return fmt.Sprintf("/%s", rest) - } - return fmt.Sprintf("/intl/%s_ALL/%s", lang, rest) -} - -func rewriteLang(wrapped http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - found := intlMatcher.FindStringSubmatch(r.URL.Path) - if found != nil { - // this was an /intl/de/... request, rewrite it - lang, rest := found[1], found[2] - r.URL.Path = pathForLangFile(lang, rest) - } - wrapped.ServeHTTP(w, r) - }) -} diff --git a/.cloudbuild/staging/firebase/firebase.json b/.cloudbuild/staging/firebase/firebase.json deleted file mode 100644 index 1a627570..00000000 --- a/.cloudbuild/staging/firebase/firebase.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "hosting": { - "public": "public", - "ignore": [ - "firebase.json", - "**/.*" - ], - "headers": [ - { - "source": "**", - "headers": [ - { - "key": "Access-Control-Allow-Origin", - "value": "*" - } - ] - } - ] - } -} \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index c150d936..00000000 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,24 +0,0 @@ ---- -name: Bug report -about: Found a bug? Let us know! -title: '' -labels: bug -assignees: '' - ---- - -**Describe the bug** -A clear and concise description of what the bug is. - -**To Reproduce** -Steps to reproduce the behavior: -1. Go to '...' -2. Click on '....' -3. Scroll down to '....' -4. See error - -**Expected behavior** -A clear and concise description of what you expected to happen. - -**Screenshots** -If applicable, add screenshots to help explain your problem. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md deleted file mode 100644 index fb1cd64d..00000000 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ /dev/null @@ -1,13 +0,0 @@ ---- -name: Feature request -about: Suggest an idea for Santa Tracker. -title: '' -labels: feature request -assignees: '' ---- - -**What would you like to see?** -A clear and concise description of what you want to appear on the site. - -**Additional context** -Add any other context or screenshots about the feature request here. diff --git a/.github/ISSUE_TEMPLATE/other.md b/.github/ISSUE_TEMPLATE/other.md deleted file mode 100644 index 96bf6bce..00000000 --- a/.github/ISSUE_TEMPLATE/other.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -name: Other -about: Questions or something else? -title: '' -labels: other -assignees: '' ---- - diff --git a/build/babel/resolve-bare-specifiers.js b/build/babel/resolve-bare-specifiers.js deleted file mode 100644 index 6e2d5640..00000000 --- a/build/babel/resolve-bare-specifiers.js +++ /dev/null @@ -1,77 +0,0 @@ -/** - * Copyright 2020 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -const fs = require('fs'); -const path = require('path'); - - -// TODO(samthor): generate on-demand -const nodeModulesPath = path.join(__dirname, '..', '..', 'node_modules'); - - -/** - * @param {string} filename that is including other files - * @return {!Object} Babel plugin - */ -module.exports = function buildResolveBareSpecifiers(filename) { - const dir = path.dirname(filename); - - const handler = (nodePath) => { - const node = nodePath.node; - if (node.source === null) { - return; - } - const specifier = node.source.value; - - if (specifier.startsWith('./') || specifier.startsWith('../')) { - return; // do nothing, is a relative URL - } - try { - new URL(specifier); - return; // do nothing, is a real URL - } catch (e) { - // ignore - } - - const ext = path.extname(specifier); - const cand = path.join(nodeModulesPath, specifier); - if (ext === '.js') { - node.source.value = path.relative(dir, cand); - return; - } - - // look for package.json in same folder, OR add a .js ext - let def; - try { - const raw = fs.readFileSync(path.join(cand, 'package.json'), 'utf8'); - def = JSON.parse(raw); - } catch (e) { - node.source.value = path.relative(dir, cand) + `.js`; - return; // best chance is just to append .js - } - - const f = def['module'] || def['jsnext:main'] || def['main'] || 'index.js'; - node.source.value = path.relative(dir, path.join(cand, f)); - }; - - return { - visitor: { - ImportDeclaration: handler, - ExportNamedDeclaration: handler, - ExportAllDeclaration: handler, - }, - }; -}; diff --git a/build/babel/template-tag-replacer.js b/build/babel/template-tag-replacer.js deleted file mode 100644 index 06801b6b..00000000 --- a/build/babel/template-tag-replacer.js +++ /dev/null @@ -1,77 +0,0 @@ -/** - * Copyright 2020 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -const babel = require('@babel/core'); -const path = require('path'); -const t = babel.types; - -/** - * @param {function(string, string): (string|undefined)} - * @return {!Object} Babel plugin - */ -module.exports = function buildTemplateTagReplacer(mapper) { - // nb. not an arrow function, so we get the context this - const handler = function(nodePath) { - const filename = this.file.opts.filename; - const dirname = path.dirname(filename); - - const {node, parent} = nodePath; - const {tag, quasi} = node; - - const qnode = quasi.quasis[0]; - if (quasi.quasis.length !== 1 || qnode.type !== 'TemplateElement') { - return; // not sure what to do here - } - const key = qnode.value.raw; - const raw = mapper(tag.name, key, dirname); - if (raw === undefined) { - return; - } else if (raw instanceof Buffer) { - // fine - } else if (typeof raw !== 'string') { - throw new TypeError(`handler returned non-string for tag '${tag.name}': ${typeof raw}`); - } - const update = raw.toString(); // catches Buffer - - // see if we're the direct child of a literal, e.g. ${_msg`foo`} - const index = (parent.expressions || []).indexOf(node); - if (parent.type !== 'TemplateLiteral' || index === -1) { - // ... we're not, just insert the string whereever - nodePath.replaceWith(t.stringLiteral(update)); - return; - } - - // merge the prev/next quasis with the updated value - const qnew = parent.quasis.slice(); - const qprev = qnew.slice(index, index + 2); - qnew.splice(index, 2, t.templateElement({ - raw: qprev[0].value.raw + update + qprev[1].value.raw, - cooked: qprev[0].value.cooked + update + qprev[1].value.cooked, - })); - - // remove the expression - const enew = parent.expressions.slice(); - enew.splice(index, 1); - - // replace the whole parent templateLiteral - const replacement = t.templateLiteral(qnew, enew); - nodePath.parentPath.replaceWith(replacement); - }; - - return { - visitor: {TaggedTemplateExpression: handler}, - }; -}; \ No newline at end of file diff --git a/build/compile-html.js b/build/compile-html.js deleted file mode 100644 index efaee453..00000000 --- a/build/compile-html.js +++ /dev/null @@ -1,85 +0,0 @@ -/** - * Copyright 2020 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -const dom = require('./dom.js'); -const {minify} = require('html-minifier'); -const terser = require('terser'); - - -/** - * @param {string} filename - * @param {{ - * compile: boolean, - * messages: function(string): string, - * body: (!Object|undefined), - * }} - */ -module.exports = async (filename, options) => { - const document = await dom.read(filename); - - // apply data-key attributes to body - if (options.body) { - Object.keys(options.body).forEach((key) => { - const value = options.body[key]; - if (value != null || value !== false) { - document.body.setAttribute(`data-${key}`, value === true ? '' : value); - } - }); - } - - // replace all [msgid] strings - const msgs = Array.from(document.querySelectorAll('[msgid]')); - msgs.forEach((node) => { - const string = options.messages(node.getAttribute('msgid')); - - if (node.localName === 'meta') { - node.setAttribute('content', string); - } else if (node.closest('head') && node.localName !== 'title') { - throw new Error(`unhandled node: ${node.localName}`); - } else { - node.innerHTML = string; - } - - node.removeAttribute('msgid'); - }); - - // return early if not compiling - if (!options.compile) { - return dom.serialize(document); - } - - const out = dom.serialize(document); - const mo = { - collapseBooleanAttributes: true, - collapseWhitespace: true, - includeAutoGeneratedTags: false, - keepClosingSlash: true, - minifyCSS: true, - minifyJS: (code) => { - // nb. html-minifier does NOT see scripts of `type="module"`, which is fine for now as they - // should be compiled away only in production anyway. - const result = terser.minify(code); - if (result.error) { - throw new Error(`terser error: ${result.error}`); - } - return result.code; - }, - removeRedundantAttributes: true, - sortAttributes: true, - sortClassName: true, - }; - return minify(out, mo); -}; diff --git a/build/compile-santa-sass.js b/build/compile-santa-sass.js deleted file mode 100644 index b6f15556..00000000 --- a/build/compile-santa-sass.js +++ /dev/null @@ -1,132 +0,0 @@ -/** - * Copyright 2020 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -const fs = require('fs'); -const path = require('path'); -const sass = require('sass'); -const url = require('url'); - -const compressed = true; - -// Define helpers that call out to native functions. Native functions can't read the current scope, -// so these exist to pass the current value of $__filename. -const fixedPreamble = `// fixed preamble for Santa SASS compilation, should never be seen -$__filename: ""; - -@function _rel($arg) { - @return -native-rel($__filename, $arg); -} -`; - -/** - * Explicitly sync function to compile SASS to CSS for Santa Tracker. - * - * This adds support for relative URL helpers in a reasonably gross way. - * - * @param {string} filename to compile - * @param {?string=} scope URL scope to position files absolutely under - * @param {string=} root to position assets relative to, null for file - */ -module.exports = (filename, scope=null, root='.') => { - const dirname = path.dirname(filename); - - if (scope === null) { - // This is the 'include CSS via ' case, where assets are relative to the loaded URL. - root = path.dirname(path.resolve(filename)); - } - - const functions = { - '-native-rel($filename, $target)': (filenameArg, targetArg) => { - const dirname = path.dirname(filenameArg.getValue()); - const found = path.join(dirname, targetArg.getValue()); - - const rel = path.relative(root, found); - const u = scope ? url.resolve(scope, rel) : rel; - - return new sass.types.String(`url(${encodeURI(u)})`); - }, - }; - - const options = { - data: `${fixedPreamble}@import "${encodeURI(filename)}"`, - sourceMap: true, - sourceMapContents: false, - sourceMapEmbed: false, - omitSourceMapUrl: true, -// sourceMapRoot: '.', - outFile: filename, // just used for relative paths - functions, - }; - if (compressed) { - options.outputStyle = 'compressed'; - } - - let result; - const sourceMapContents = {}; - const originalReadFileSync = fs.readFileSync; - try { - // yes -- really. Apologies to those reading this in future. - // This magic ensures that $__filename is set to the currently executing file during its run. - // We save the real source contents to insert into the sourceMap later. - fs.readFileSync = (p, o) => { - if (o !== 'utf8') { - throw new Error('expected dart-sass to read with options=utf8'); - } - if (!path.isAbsolute(p)) { - throw new Error(`expected dart-sass to read absolute URL: ${p}`) - } - - const fileContents = originalReadFileSync(p, o); - sourceMapContents[p] = fileContents; - - // This hack works because source maps are only per-line, and the prefix here doesn't effect - // a browser's ability to map back to the original source. - return `$__held_filename:$__filename;$__filename:"${p}";${fileContents} -$__filename:$__held_filename;`; // must be on newline to prevent comments leaking - }; - result = sass.renderSync(options); - } finally { - fs.readFileSync = originalReadFileSync; - } - - const map = JSON.parse(result.map.toString()); - - map.sourcesContent = []; - map.sources = map.sources.map((source) => { - // SASS sometimes returns us absolute files that probably start with file://. - if (source.startsWith('file://')) { - source = source.substr(7); - - if (!path.isAbsolute(source)) { - throw new Error(`had unexpected relative URL from sass: ${source}`); - } - } - - // If this isn't absolute then it's actually relative to the original filename, not to the - // current working directory (which is what path.resolve would use). - if (!path.isAbsolute(source)) { - source = path.join(dirname, source); - } - - map.sourcesContent.push(sourceMapContents[source] || null); - return path.relative(dirname, source); - }); - - return { - code: result.css.toString(), - map, - }; -}; \ No newline at end of file diff --git a/build/compile-scene.js b/build/compile-scene.js deleted file mode 100644 index d395a4d1..00000000 --- a/build/compile-scene.js +++ /dev/null @@ -1,178 +0,0 @@ -/** - * Copyright 2020 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -const closureCompiler = require('google-closure-compiler'); -const closureCompilerUtils = require('google-closure-compiler/lib/utils.js'); -const fs = require('fs').promises; -const tmp = require('tmp'); - -const EXTERNS = [ - 'build/transpile/magic-externs.js', - 'node_modules/google-closure-compiler/contrib/externs/maps/google_maps_api_v3_exp.js', - 'node_modules/google-closure-compiler/contrib/externs/jquery-3.3.js', -]; - -const CLOSURE_DISABLE_WARNINGS = [ - // Causes complaints about misordered goog.require(). - 'underscore', - - // Lots of library code generates unused vars. - 'unusedLocalVariables', - - // This includes checks for: missing semicolons, missing ? or ! on object types, etc. - // This would be a lot of work to resolve. - 'lintChecks', - - // These have to do with goog.require()/goog.provide() and how we "leak" some objects (such as - // Constants, LevelUp, etc). These could be fixed up. - 'missingSourcesWarnings', - 'extraRequire', - 'missingProvide', - 'strictMissingRequire', - 'missingRequire', -]; - -const syntheticSourceRe = /\[synthetic:(.*?)\]/; - - -/** - * Process a raw source map, including adding source file contents. This is a tiny performance hit - * and creates a HUGE source map, but it should only be served in dev. - * - * @param {!Buffer} buf raw sourceMap to process - * @param {string} root path to apply to sourceMap - * @return {!Object} updated sourceMap containing all source file contents - */ -async function processSourceMap(buf, root='../../') { - const o = JSON.parse(buf.toString()); - o.sourceRoot = root; - o.sourcesContent = []; - for (let i = 0; i < o.sources.length; ++i) { - const source = o.sources[i]; - - // This is an ES6 synthetic source file for transpilation. Ignore it, but it still has to be - // returned so the source map isn't confused. - const m = syntheticSourceRe.exec(source); - if (m) { - o.sources[i] = ''; - o.sourcesContent.push(''); - continue; - } - - const buf = await fs.readFile(source, 'utf8'); - o.sourcesContent.push(buf); - } - return o; -} - - -/** - * @param {!closureCompiler.compiler} compiler - * @return {!Promise} - */ -function invokeCompiler(compiler) { - return new Promise((resolve) => { - const compilerCallback = (status, stdout, stderr) => { - resolve({status, code: stdout, log: stderr}); - }; - compiler.run(compilerCallback); - }); -} - - -/** - * @param {string} sceneName - * @param {boolean=} compile - * @return {{code: string, map: !Object}} - */ -module.exports = async function compile(sceneName, compile=true) { - const compilerSrc = [ - 'build/transpile/export.js', - 'static/scenes/_shared/js', - `static/scenes/${sceneName}/js`, - '!**_test.js', - ]; - - // Scenes that require the Closure Library need to be compiled (and will fail if compile is - // false). For others, we can provide a quick fake base.js that includes basic polyfills for - // goog.require()/goog.provide() and friends. - if (!compile) { - compilerSrc.unshift('build/transpile/base.js'); - } - - // This function works by compiling scenes with Closure and then re-exporting them as ES modules. - // We expect `app.Game` to be provided (see build/transpile.export.js), and place it on the var - // `_globalExport` (see below). - // Additionally, import `_msg` and `_static` from our magic script. This lets scenes interact - // with their environment (although Rollup will complain if they're not used). - const outputWrapper = - 'import {_msg, _static} from \'../../src/magic.js\';' + - 'var _globalExport;(function(){%output%}).call(self);export default _globalExport;'; - - // Create a temporary place to store the source map. Closure can only write this to a real file. - const sourceMapTemp = tmp.fileSync(); - - const compilerFlags = { - js: compilerSrc, - externs: EXTERNS, - create_source_map: sourceMapTemp.name, - assume_function_wrapper: true, - dependency_mode: 'STRICT', // ignore all but exported via _globalExportEntry, below - entry_point: '_globalExportEntry', - compilation_level: compile ? 'SIMPLE_OPTIMIZATIONS' : 'WHITESPACE_ONLY', - warning_level: 'VERBOSE', - language_in: 'ECMASCRIPT_NEXT', - language_out: 'ECMASCRIPT_2019', - process_closure_primitives: true, - jscomp_off: CLOSURE_DISABLE_WARNINGS, - output_wrapper: outputWrapper, - rewrite_polyfills: false, - inject_libraries: true, // injects $jscomp when using the Closure Library, harmless otherwise - use_types_for_optimization: true, - }; - - try { - const compiler = new closureCompiler.compiler(compilerFlags); - - // Use any native image available, as this can be up to 10x (!) speed improvement on Java. - const nativeImage = closureCompilerUtils.getNativeImagePath(); - if (nativeImage) { - console.warn(`Compiling Closure scene ${sceneName} with native image...`); - compiler.JAR_PATH = undefined; - compiler.javaPath = nativeImage; - } else { - console.warn(`Compiling Closure scene ${sceneName} with Java (unsupported platform=${process.platform})...`); - } - - const {status, code, log} = await invokeCompiler(compiler); - if (log.length) { - console.warn(`# ${sceneName}\n${log}`); - } - if (status) { - throw new Error(`failed to compile ${sceneName}: ${code}`); - } - - const map = await processSourceMap(await fs.readFile(sourceMapTemp.name)); - - // nb. used so that listening callers can watch the whole dir for changes. - map.sources.push(`static/scenes/${sceneName}/js`, `static/scenes/_shared/js`); - map.sourcesContent.push(null, null); - - return {code, map}; - } finally { - sourceMapTemp.removeCallback(); - } -}; diff --git a/build/dom.js b/build/dom.js deleted file mode 100644 index 524217d4..00000000 --- a/build/dom.js +++ /dev/null @@ -1,79 +0,0 @@ -/** - * Copyright 2020 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -const fsp = require('./fsp.js'); -const parse5 = require('parse5'); -const parser = new (require('jsdom/lib/jsdom/living')).DOMParser(); - -/** - * Minimal adapter for JSDom. - */ -const treeAdapter = { - getFirstChild: (node) => node.childNodes[0], - getChildNodes: (node) => node.childNodes, - getParentNode: (node) => node.parentNode, - getAttrList: (node) => node.attributes, - getTagName: (node) => node.tagName.toLowerCase(), - getNamespaceURI: (node) => node.namespaceURI || 'http://www.w3.org/1999/xhtml', - getTemplateContent: (node) => node.content, - getTextNodeContent: (node) => node.nodeValue, - getCommentNodeContent: (node) => node.nodeValue, - getDocumentTypeNodeName: (node) => node.name, - getDocumentTypeNodePublicId: (node) => doctypeNode.publicId || null, - getDocumentTypeNodeSystemId: (node) => doctypeNode.systemId || null, - isTextNode: (node) => node.nodeName === '#text', - isCommentNode: (node) => node.nodeName === '#comment', - isDocumentTypeNode: (node) => node.nodeType === 10, - isElementNode: (node) => Boolean(node.tagName), -}; - -/** - * Parse the input into a JSDom document. - * - * @param {string|!Buffer} src - * @return {!Document} - */ -function parse(src) { - return parser.parseFromString(src.toString(), 'text/html'); -} - -module.exports = { - parse, - - /** - * Parse the file into a JSDom document. - * - * @param {string} filename - * @return {!Promise} - */ - async read(filename) { - const raw = await fsp.readFile(filename, 'utf8'); - return parse(raw); - }, - - /** - * Seralize the JSDom document or node. - * - * @param {!Node|!Document} node - */ - serialize(node) { - if ('innerHTML' in node) { - return node.innerHTML; - } - return parse5.serialize(node, {treeAdapter}); - }, - -}; diff --git a/build/fsp.js b/build/fsp.js deleted file mode 100644 index 786d5b11..00000000 --- a/build/fsp.js +++ /dev/null @@ -1,65 +0,0 @@ -/** - * Copyright 2020 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -const fsRaw = require('fs'); -const fs = fsRaw.promises; -const rimraf = require('rimraf'); - -module.exports = Object.assign({}, fs, { - - /** - * @param {string} f to load stats for - * @return {Promise} - */ - async statOrNull(f) { - return fs.stat(f).catch((err) => null); - }, - - /** - * @param {string} f to check - * @return {Promise} does this file exits? - */ - async exists(f) { - return this.statOrNull(f).then((out) => out !== null); - }, - - /** - * @param {string} dir to create - * @return {Promise} - */ - mkdirp(dir) { - return fs.mkdir(dir, {recursive: true}); - }, - - /** - * @param {string} f to unlink - * @return {Promise} - */ - unlinkAll(f) { - if (fs.rm) { - return fs.rm(f, { recursive: true, force: true }); - } - - // We keep rimraf around for Node < 14.14, as `fs.rm` was only added then. - // Don't use util.promisify as we pass options to disable glob. - return new Promise((resolve, reject) => { - rimraf(f, {glob: false}, (err) => { - err ? reject(err) : resolve(); - }); - }); - }, - -}); diff --git a/build/git-version.js b/build/git-version.js deleted file mode 100644 index 2d93c732..00000000 --- a/build/git-version.js +++ /dev/null @@ -1,48 +0,0 @@ -/** - * Copyright 2021 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -const git = require('git-last-commit'); -const { default: fetch } = require('node-fetch'); - -/** - * @returns {Promise} - */ -const getDeployedVersion = async () => { - const text = await fetch('https://santa-staging.appspot.com/hash') - .then((res) => (res.ok ? res.text() : '')); - // This looks like "git_hash:build_version", e.g., "ab123141f1e:v20211123...", and we only want - // the git hash on the left. - return text.split(':')[0]; -}; - -/** - * @return {Promise} - */ -const getCurrentVersion = () => { - return new Promise((resolve) => { - git.getLastCommit((err, commit) => { - if (err) { - console.warn(`Could not retrieve current git revision`, err) - } - resolve(commit && commit.hash || ''); - }); - }); -}; - -module.exports = { - getDeployedVersion, - getCurrentVersion, -}; diff --git a/build/glob-all.js b/build/glob-all.js deleted file mode 100644 index a3b704de..00000000 --- a/build/glob-all.js +++ /dev/null @@ -1,51 +0,0 @@ -/** - * Copyright 2020 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -const glob = require('glob'); - -/** - * Performs a synchronous glob over all requests, supporting Closure's negation syntax. e.g.: - * 'foo*', '!foo-bar' => returns all `foo*` but not `foo-bar` - * - * If a non-magic glob (i.e., no * or glob charaters) doesn't match a file, then this method - * throws Error. - * - * @param {...string} req - * @return {!Array} - */ -module.exports = (...req) => { - const out = new Set(); - const options = {mark: true}; - - for (let cand of req) { - const negate = cand[0] === '!'; - if (negate) { - cand = cand.substr(1); - } - - const result = glob.sync(cand, options); - if (!result.length && !glob.hasMagic(cand)) { - throw new Error(`couldn't match file: ${cand}`); - } - - for (const each of result) { - negate ? out.delete(each) : out.add(each); - } - } - - // filter out directories - return [...out].filter((cand) => !cand.endsWith('/')); -}; diff --git a/build/group.js b/build/group.js deleted file mode 100644 index 21e0b3db..00000000 --- a/build/group.js +++ /dev/null @@ -1,75 +0,0 @@ -/** - * Copyright 2020 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -const os = require('os'); -const cpuCount = os.cpus().length; - -class WorkGroup { - constructor(tasks) { - tasks = ~~tasks; - if (tasks <= 0) { - throw new TypeError('must have at least one task'); - } - this._tasks = tasks; - this._used = 0; - - this._releasePromise = null; - this._resolveRelease = () => {}; - } - - async work(fn) { - await this._take(); - try { - return await fn(); - } finally { - this._done(); - } - } - - async _take() { - for (;;) { - if (this._used < this._tasks) { - ++this._used; - - if (this._used === this._tasks) { - this._releasePromise = new Promise((resolve) => { - this._resolveRelease = resolve; - }); - } - - return true; - } - - await this._releasePromise; - } - } - - _done() { - if (this._used === 0) { - throw new TypeError(`returned too many tasks`); - } - - const shouldResolve = (this._used === this._tasks); - --this._used; - - shouldResolve && this._resolveRelease(); - } -} - -module.exports = (tasks = cpuCount*0.75) => { - const w = new WorkGroup(tasks) - return (fn) => w.work(fn); -}; diff --git a/build/i18n.js b/build/i18n.js deleted file mode 100644 index 8cd3ed38..00000000 --- a/build/i18n.js +++ /dev/null @@ -1,94 +0,0 @@ -/** - * Copyright 2020 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -const fs = require('fs'); -const path = require('path'); -const Entities = require('html-entities').AllHtmlEntities; - -const entities = new Entities(); -const emptyFunc = () => {}; -const fallback = require('../en_src_messages.json'); - -/** - * @param {string} lang - * @param {function(string): ?string} callback - * @return {function(?string): string} - */ -function lookup(lang, callback=emptyFunc) { - const data = require(`../_messages/${lang}.json`); - - // Support e.g. "fr" for "fr-CA", or "es" for "es-419". - let similarLangData = null; - const similarLang = lang.split('-')[0]; - if (similarLang !== lang) { - try { - similarLangData = require(`../_messages/${similarLang}.json`) - } catch (e) { - similarLangData = null; - } - } - - return (msgid) => { - if (msgid === null) { - return lang; - } - - let o = data[msgid]; - if (!o) { - const out = callback(msgid); - if (typeof out === 'string') { - return out; - } else if (out !== undefined) { - return '?'; - } - o = (similarLangData ? similarLangData[msgid] : null) || fallback[msgid] || null; - } - - if (o && o['raw']) { - // This is a fallback message, so tease out the actual string. Each contains real - // text and an optional . - const r = o['raw']; - return r.replace(/(.*?)<\/ph>/g, (match, part) => { - // remove if we find it - part = part.replace(/.*?<\/ex>/g, ''); - if (!part) { - throw new Error(`got invalid part for raw string: ${r}`); - } - - return entities.decode(part); - }); - } - - return o && (o['message'] || o['raw']) || '?'; - }; -} - -let langCache; - -lookup.all = function(callback=emptyFunc) { - if (langCache === undefined) { - const cands = fs.readdirSync(path.join(__dirname, '..', '_messages')); - langCache = cands.map((file) => file.split('.')[0]); - } - - const out = {}; - langCache.forEach((lang) => { - out[lang] = lookup(lang, (msgid) => callback(lang, msgid)); - }); - return out; -}; - -module.exports = lookup; \ No newline at end of file diff --git a/build/import-utils.js b/build/import-utils.js deleted file mode 100644 index 4e8e05a7..00000000 --- a/build/import-utils.js +++ /dev/null @@ -1,138 +0,0 @@ -/** - * Copyright 2020 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -const path = require('path'); - -const alreadyResolvedMatch = /^(\.{0,2}\/|[a-z]\w*\:)/; // matches start of './' or 'https:' etc - -if (path.sep !== '/') { - throw new Error(`importUtils is unsupported on Windows (path.sep=${path.sep})`); -} - -module.exports = { - - /** - * Returns whether the given string is a URL or URL-like. - * - * @param {string|?URL} cand - * @return {boolean} - */ - isUrl(cand) { - if (cand instanceof URL || cand.startsWith('//')) { - return true; - } else if (!cand) { - return false; - } - - try { - new URL(cand); // doesn't allow "//-prefix" - return true; - } catch (e) { - // ignore - } - return false; - }, - - /** - * Returns the pathname of this URL, or the string itself (if not a URL). - * - * @param {string|!URL} cand to check - * @param {string} root optional root if not a URL - * @return {string} - */ - pathname(cand, root='/') { - if (this.isUrl(cand)) { - return new URL(cand).pathname; - } else if (path.isAbsolute(cand)) { - return cand; // looks like "/foo" - } else { - return path.join(root, cand); - } - }, - - /** - * Join a URL path or other components. - * - * @param {string|!URL} cand - * @param {string} rest - * @return {string} - */ - join(cand, rest) { - if (this.isUrl(cand)) { - const u = new URL(rest, cand); // order is addition, then base - return u.toString(); - } - return path.join(cand, rest); - }, - - /** - * Joins a URL path or other components, but ensures a trailing slash. - * - * @param {string|!URL} cand - * @param {string} rest - * @return {string} - */ - joinDir(cand, rest) { - const out = rest ? this.join(cand, rest) : cand; - if (!out.endsWith('/')) { - return `${out}/`; - } - return out; - }, - - /** - * Is the passed candidate string already fully resolved? - * - * @param {string|?URL} cand - * @return {boolean} - */ - alreadyResolved(cand) { - if (cand instanceof URL) { - return true; - } else if (!cand) { - return false; - } - // TODO(samthor): can ES modules import "//domain.com/blah.js"? - return Boolean(alreadyResolvedMatch.exec(cand)); - }, - - /** - * Ensure that the specified ID is suitable for import as an ES6 module path. - * - * @param {string|!URL} cand - * @return {string} - */ - relativize(cand) { - if (this.alreadyResolved(cand)) { - return cand.toString(); - } - return `./${cand}`; - }, - - /** - * Builds an ES6 module which simply imports the given targets for their side-effects. - * - * @param {...string} resources - * @return {string} - */ - staticImport(...resources) { - return resources.map((resource) => { - // TODO(samthor): escape resource. - return `import '${this.relativize(resource)}';\n`; - }).join(''); - }, - -}; \ No newline at end of file diff --git a/build/modern-builder.js b/build/modern-builder.js deleted file mode 100644 index e6293663..00000000 --- a/build/modern-builder.js +++ /dev/null @@ -1,141 +0,0 @@ -/** - * Copyright 2020 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -const rollup = require('rollup'); -const transformFutureModules = require('./transform-future-modules.js'); -const rollupPluginCJS = require('@rollup/plugin-commonjs'); -const {default: rollupPluginNode} = require('@rollup/plugin-node-resolve'); -const path = require('path'); -const importUtils = require('./import-utils.js'); -const fs = require('fs'); - - -/** - * @param {{[id: string]: string|undefined}} entrypoints undefined to use loader, string to use this code - */ -module.exports = async (entrypoints, options) => { - options = Object.assign({ - loader: () => {}, - external: () => {}, - metaUrlScope: null, - commonJS: false, - workDir: null, - format: 'esm', - }, options); - - /** @type {{[id: stirng]: string}} */ - let loadCache = {}; - - const virtualPlugin = { - async load(idToLoad) { - if (idToLoad.startsWith('\0')) { - return; - } - - if (idToLoad.startsWith('/')) { - idToLoad = path.relative(process.cwd(), idToLoad); - } - - // If we were provided code for this entrypoint, return it first. - const entrypoint = entrypoints[idToLoad]; - if (entrypoint) { - return entrypoint; - } - - // We just resolved this, so use the cache. - if (idToLoad in loadCache) { - return loadCache[idToLoad]; - } - - return options.loader(idToLoad); - }, - transform(content, id) { - if (!id.endsWith('.js')) { - return transformFutureModules(id, content.code || content); - } - }, - async resolveId(importee, importer) { - let id = undefined; - - if (importer === undefined) { - id = importee; - } else if (importee.match(/^\.{1,2}\//)) { - id = path.join(path.dirname(importer), importee); - - // This looks like something that's relative or resolved, but it might not be, perhaps - // because it's a commonJS import that has no suffix. - // See if our loader understands it, if so, cache and return. - if (!fs.existsSync(id)) { - const checkLoad = await options.loader(id); - if (!checkLoad) { - return; - } - loadCache[id] = checkLoad; - } - } - - // support marking depedencies as external with a possible rewrite - if (id) { - const ret = options.external(id); - if (ret) { - if (typeof ret === 'string') { - id = ret; - } - return {id, external: true}; - } - } - - return id; - }, - resolveImportMeta(prop, {moduleId}) { - if (prop !== 'url') { - throw new TypeError(`got unsupported import.meta.${prop} request for: ${moduleId}`); - } else if (!options.metaUrlScope) { - throw new TypeError(`import.meta.url request without metaUrlScope: ${moduleId}`); - } - - const rel = path.relative(options.workDir, moduleId); - const output = importUtils.join(options.metaUrlScope, rel); - - // TODO(samthor): escape - return `'${output}'`; - }, - }; - - const input = {}; - Object.keys(entrypoints).forEach((id) => { - input[id] = id; - }); - - const resolveNodePlugin = rollupPluginNode({ browser: true }); - const plugins = [virtualPlugin, resolveNodePlugin]; - if (options.commonJS) { - plugins.unshift(rollupPluginCJS({ extensions: [ '.js', '.mjs' ]})); - } - - const bundle = await rollup.rollup({input, plugins}); - - const generated = await bundle.generate({ - format: options.format, - entryFileNames: '[name]', // we expect .js to be provided - chunkFileNames: path.join(options.workDir || '', '_[hash].js'), - }); - - // console.info('got bundle of modules', Object.keys(generated.output).length); - // console.debug(generated.output); - - return generated.output; -}; diff --git a/build/modern-loader.js b/build/modern-loader.js deleted file mode 100644 index 0100e249..00000000 --- a/build/modern-loader.js +++ /dev/null @@ -1,130 +0,0 @@ -/** - * Copyright 2020 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -const importUtils = require('./import-utils.js'); -const path = require('path'); -const rollup = require('rollup'); -const transformFutureModules = require('./transform-future-modules.js'); -const esmResolve = require('esm-resolve').default; - - -/** @type {(id: string) => string} */ -const ensureModuleQuery = (id) => { - if (!id || importUtils.isUrl(id)) { - return id; - } - const prev = id.split('?')[0]; - return prev + '?module'; -}; - - -/** - * Rewrites the given file to operate correctly as an ES6 module. This is not used in production. - * - * While this (currently) internally uses Rollup, it does not read file contents from disk, or have - * any knowledge of external modules (aside resolving the correct path to node_modules/ content). - * - * @param {string} id - * @param {string|{code: string, map: Object}} content - * @param {(warn: rollup.RollupWarning) => void} onwarn - * @return {{code: string, map: SourceMap}|null} - */ -module.exports = async (id, content, onwarn = () => {}) => { - id = path.resolve(id); - - // If this is not a JS file, then skip Rollup and just rewrite it for the module case. - // The "transformFutureModules" handles files like ".json", which we should be able to import. - const ext = path.extname(id); - if (ext !== '.js' && ext !== '.mjs') { - if (!content) { - throw new Error(`got no content for: ${id}`); - } - - const transformed = await transformFutureModules(id, content.code || content); - if (!transformed) { - return; - } - content = { code: transformed.code }; - - // If we transform a HTML file into a HTML module, we get back JavaScript, but there can be - // further imports. Only bail out here if we had some other type. - if (transformed && !transformed.needsModuleRewrite) { - return content; - } - } - - /** @type {rollup.Plugin} */ - const virtualPlugin = { - name: 'modern-loader', - - load(idToLoad) { - idToLoad = idToLoad.split('?')[0]; - if (idToLoad !== id) { - throw new Error(`got load request for non-main ID: ${idToLoad}`); - } - return content; - }, - - resolveId(importee, importer) { - // Resolve ourselves, and anything that Rollup doesn't need to (./, ../, etc). - if (importee === id || importUtils.alreadyResolved(importee)) { - return ensureModuleQuery(importee); - } - - // This isn't valid (null prefix is used to say "don't touch me"). - if (importee.startsWith('\0') || !importer) { - return; - } - - // Otherwise, use our custom Node resolver. This works around issues in the defacto standard - // module 'rollup-plugin-node-resolve', such as: - // * lets us point to the nearest node_modules/ only (including a symlink) - // * resolved IDs that return as an object aren't passed to .external (below) - const resolver = esmResolve(importer, { allowMissing: true }); - const resolved = resolver(importee); - return ensureModuleQuery(resolved); - }, - }; - - // Rollup can be relatively slow (~10's of ms) but often this is happening in parallel. We are - // literally only here to rewrite imports into node_modules. This can probably be done faster. - const bundle = await rollup.rollup({ - input: id, - plugins: [virtualPlugin], - external(id, parentId, isResolved) { - if (isResolved) { - return true; - } - }, - // This is true for sanity as Rollup never even gets to load anything but the primary module, - // so we should only end up with a single result (checked below). - preserveModules: true, - onwarn, - treeshake: false, // we don't want any code thrown away in dev - }); - - const out = await bundle.generate({ - name: id, - format: 'es', - sourcemap: true, - }); - - if (out.output.length !== 1) { - throw new Error(`unexpected Rollup length: ${out.output.length}`); - } - const first = out.output[0]; - return {code: first.code, map: first.map}; -}; diff --git a/build/modern-vfs-middleware.js b/build/modern-vfs-middleware.js deleted file mode 100644 index 960fc17b..00000000 --- a/build/modern-vfs-middleware.js +++ /dev/null @@ -1,116 +0,0 @@ -/** - * Copyright 2020 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -const modernLoader = require('./modern-loader.js'); -const mimeTypes = require('mime-types'); -const path = require('path'); -const fs = require('fs').promises; -const polka = require('polka'); -const { statOrNull } = require('../build/fsp.js'); - - -const debug = false; - - -/** - * Builds and returns HTTP middleware that defers to a virtual loader and serves as ES6 modules. - * - * @param {(id: string) => Promise} vfsLoad - * @param {string?} prefix - * @return {polka.Middleware} - */ -module.exports = (vfsLoad, prefix=null) => { - return async (req, res, next) => { - const headers = { - 'Access-Control-Allow-Origin': '*', // always CORS enabled - 'Expires': '0', - 'Cache-Control': 'no-store', - }; - const end = (status, data) => { - res.writeHead(status, headers); - return res.end(data); - }; - - // nb. Any fetches with "?" for supported file types will disable transforming them. - const id = path.join(prefix || '.', req.path.substr(1)); - let isModuleMode = Boolean(req.headers['origin'] && req.headers['referer'] && !req.search); - if (req.search === '?module') { - isModuleMode = true; - } - - const stat = await statOrNull(id); - if (stat && !stat.isFile()) { - return next(); // exists and is not a regular file, defer to next - } - - let content; - let isVirtual = false; - - if (stat === null) { - try { - debug && console.warn('loading vfs', id); - content = await vfsLoad(id); - } catch (e) { - // TODO: pass to caller, internal error in VFS - console.warn('vfs', e); - return end(500); - } - if (content == null) { - return end(404); - } - isVirtual = true; - } - - if (!isModuleMode) { - if (!isVirtual) { - return next(); // defer to next handler, this is just a real file - } - - // If this was a regular fetch of a virtual file, just serve it (generated CSS/JS/etc). - // TODO: insert sourceMap - const raw = (typeof content === 'string') ? content : content.code; - const mimeType = mimeTypes.lookup(id); - if (mimeType) { - headers['Content-Type'] = mimeType; - } - return end(200, raw); - } - - if (!isVirtual) { - content = await fs.readFile(id, 'utf-8'); - } - - // Ask the modern loader to rewrite this single file (virtual or not is moot here). - let result = null; - try { - result = await modernLoader(id, content, (warn) => { - console.warn(id, warn.toString()); - }); - } catch (e) { - // TODO: pass to caller, internal error in loader - console.warn('loader', e); - return end(500); - } - if (result == null) { - // TODO(samthor): We should decide on this earlier, before we read the file from disk. - return next(); // can't be rewritten to a module: wrong type, serve file as-is - } - debug && console.warn('rewritten', id); - - headers['Content-Type'] = 'application/javascript'; - return end(200, result.code); - }; -}; \ No newline at end of file diff --git a/build/release-html.js b/build/release-html.js deleted file mode 100644 index 411ae4a7..00000000 --- a/build/release-html.js +++ /dev/null @@ -1,158 +0,0 @@ -/** - * Copyright 2020 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -const fsp = require('./fsp.js'); -const {minify} = require('html-minifier'); -const terser = require('terser'); -const {JSDOM} = require('jsdom'); - - -const emptyFunc = () => {}; - - -function compileHtml(raw) { - const mo = { - collapseBooleanAttributes: true, - collapseWhitespace: true, - includeAutoGeneratedTags: false, - keepClosingSlash: true, - minifyCSS: true, - minifyJS: (code) => { - // nb. html-minifier does NOT see scripts of `type="module"`, which is fine for now as they - // should be compiled away only in production anyway. - const result = terser.minify(code); - if (result.error) { - throw new Error(`terser error: ${result.error}`); - } - return result.code; - }, - removeComments: true, - removeEmptyAttributes: true, - removeRedundantAttributes: true, - sortAttributes: true, - sortClassName: true, - }; - return minify(raw, mo); -} - - -/** - * Apply the given attribute to the passed Node. - * - * @param {!Node} node - * @param {string} attrName - * @param {(string|number|boolean|null)=} value - */ -function applyAttribute(node, attrName, value=true) { - if (value != null || value !== false) { - node.setAttribute(attrName, value === true ? '' : '' + value); - } else { - node.removeAttribute(attrName); - } -} - - -/** - * @param {!Array} qs - * @param {string} attrName - * @param {(string|number|boolean|null)=} value - * @return {!Array} matched nodes - */ -function applyAttributeToAll(node, qs, attrName, value=true) { - const out = []; - qs.forEach((q) => { - const nodes = node.querySelectorAll(qs); - nodes.forEach((node) => applyAttribute(node, attrName, value)); - out.push(...nodes); - }); - return out; -} - - -/** - * Apply the given i18n string to the passed Node. - * - * @param {?string} string - * @param {!Node} node - */ -function applyToNode(string, node) { - if (node.localName === 'meta') { - if (string === null) { - node.removeAttribute('content'); - } else { - node.setAttribute('content', string); - } - } else if (node.closest('head') && node.localName !== 'title') { - throw new Error(`unhandled node: ${node.localName}`); - } else { - node.innerHTML = (string !== null ? string : ''); - } -} - - -/** - * Builds a helper that applies the specified language to the passed document. - * - * @param {!JSDOM} dom - * @return {function((function(string): string|null)): string} - */ -function buildApplyLang(dom) { - const document = dom.window.document; - const messagesNodeMap = new Map(); - - // Find and contain every [msgid] that needs replacing. - document.querySelectorAll('[msgid]').forEach((node) => { - messagesNodeMap.set(node, node.getAttribute('msgid')); - node.removeAttribute('msgid'); - }); - - const applyMessages = (messages) => { - messagesNodeMap.forEach((msgid, node) => { - const string = (messages ? messages(msgid) : ''); - applyToNode(string, node); - }); - }; - - return (messages) => { - // set - const lang = messages(null); // null message returns lang - applyAttribute(document.documentElement, 'lang', lang); - applyMessages(messages); - return dom.serialize(); - }; -} - - -module.exports = { - applyAttribute, - applyAttributeToAll, - buildApplyLang, - - async dom(filename) { - const raw = await fsp.readFile(filename, 'utf8'); - const source = compileHtml(raw); - return new JSDOM(source); - }, - - async load(filename, rewriter=emptyFunc) { - const dom = await this.dom(filename); - - await rewriter(dom.window.document); - - return buildApplyLang(dom); - }, - -}; diff --git a/build/source-magic.js b/build/source-magic.js deleted file mode 100644 index 87440d05..00000000 --- a/build/source-magic.js +++ /dev/null @@ -1,175 +0,0 @@ -/** - * Copyright 2020 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -const babel = require('@babel/core'); -const t = babel.types; -const path = require('path'); -const importUtils = require('./import-utils.js'); - -/* -TODO(samthor): This is fine but can be done differently. - -Right now, this identifies certain named imports as magic. The AST parser below identifies their -use before storing them and letting us replace them at a later point in time (for i18n). In this -approach, we prevent use of imports in any way _but_ a TaggedTemplateExpression. - -The alternative is to provide a fake import (that gets rolled up by Rollup "properly") that ends -up including real methods that we can resolve with Babel's evaluate helpers. This has a benefit in -that we can 'follow' the helper around, e.g.: - - const ast = await babel.parseAsync('const _msg=function __magic_msg(){}; var x = _msg; var q = x; q`foo`;'); - traverse.default(ast, { - Identifier(nodePath) { - const name = nodePath.node.name; - if (nodePath.parentPath.node.type !== 'TaggedTemplateExpression') { - return; - } - - const adjacent = nodePath.parentPath.insertBefore(babel.types.identifier(name))[0]; - const evaluated = adjacent.evaluate(); - if (evaluated.confident) { - throw new Error('babel should not evaluate this'); - } - const node = evaluated.deopt.node; - if (node.type !== 'FunctionExpression') { - throw new Error('expected magic function, was: ' + node.type); - } - - const id = node.id; - if (id) { - console.warn('name=', id.name); - } else { - console.warn('no name'); - } - - // console.info(evaluated.deopt.node); - }, - }); -*/ - -module.exports = () => { - const magicImportNodes = new Set(); - const importDeclarations = new Map(); - const tagged = {}; - - const matchesImportNode = (taggedTemplateNodePath) => { - const tagNodePath = taggedTemplateNodePath.get('tag'); - - for (const importNodePath of magicImportNodes) { - const v = importNodePath.node.source.value; - const r = tagNodePath.referencesImport(v, tagNodePath.node.name); - if (r) { - return true; - } - } - return false; - }; - - /** - * Ensure that the given name exists inside the tagged map. - * - * @param {string} - * @return {!Map<*, *>} - */ - const getTagged = (name) => { - const prev = tagged[name]; - if (prev !== undefined) { - return prev; - } - const update = new Map(); - tagged[name] = update; - return update; - }; - - const plugin = { - pre(state) { - if (this.run) { - throw new Error(`can only run magic plugin once`); - } - this.run = true; - }, - post(state) { - // If this crashes, it's probably because another plugin removed the `import` declaration. - magicImportNodes.forEach((nodePath) => nodePath.remove()); - }, - visitor: { - ImportDeclaration(nodePath) { - const {node} = nodePath; - if (node.source.value === '__magic') { - magicImportNodes.add(nodePath); - } else { - importDeclarations.set(nodePath, node.source.value); - } - }, - - TaggedTemplateExpression(nodePath) { - if (!matchesImportNode(nodePath)) { - return; - } - - const taggedNode = nodePath.node; - const name = taggedNode.tag.name; - const {quasi} = taggedNode; - - // Confirm that we look like "_foo`bar`" without ${}'s - const qnode = quasi.quasis[0]; - if (quasi.quasis.length !== 1 || qnode.type !== 'TemplateElement') { - throw new TypeError(`got non-static magic import replacer`); - } - const key = qnode.value.raw; - - // Just replace with something that we'll notice if we miss. - nodePath.replaceWith(t.nullLiteral()); - - const all = getTagged(name); - all.set(nodePath, key); - }, - }, - }; - - return { - plugin, - - seen(name) { - return name in tagged; - }, - - visit(id, visitor) { - const dir = path.dirname(id); - - for (const name in tagged) { - const all = tagged[name]; - all.forEach((key, nodePath) => { - const update = visitor.taggedTemplate(name, key); - if (typeof update !== 'string') { - throw new Error(`expected taggedTemplate string update, got ${update}`); - } - nodePath.replaceWith(t.stringLiteral(update)); - }); - }; - - importDeclarations.forEach((value, nodePath) => { - const resolved = path.join(dir, value); - const update = visitor.rewriteImport(resolved); - if (update) { - const rel = importUtils.relativize(path.relative(dir, update)); - const sourcePath = nodePath.get('source'); - sourcePath.replaceWith(t.stringLiteral(rel)); - } - }); - }, - }; -}; \ No newline at end of file diff --git a/build/terser-worker.js b/build/terser-worker.js deleted file mode 100644 index 3feef05b..00000000 --- a/build/terser-worker.js +++ /dev/null @@ -1,28 +0,0 @@ -/** - * Copyright 2020 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -const { - isMainThread, parentPort, workerData -} = require('worker_threads'); - -const Terser = require('terser'); - -if (isMainThread) { - throw new Error('Only supports running on worker thread'); -} - -const result = Terser.minify(workerData); -parentPort.postMessage(result); diff --git a/build/transform-future-modules.js b/build/transform-future-modules.js deleted file mode 100644 index 3e0f5f16..00000000 --- a/build/transform-future-modules.js +++ /dev/null @@ -1,56 +0,0 @@ -/** - * Copyright 2020 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -const path = require('path'); -const htmlModules = require('html-modules-polyfill'); - -/** - * Converts the given source code into a polyfilled module version of that code. - * - * @param {string} id path to file - * @param {string} code to convert - * @return {Promise} transformed code, or null for not supported - */ -module.exports = async (id, code) => { - const ext = path.extname(id); - - switch (ext) { - case '.css': { - // This implements CSS Modules as described: - // https://github.com/w3c/webcomponents/blob/gh-pages/proposals/css-modules-v1-explainer.md - // https://twitter.com/argyleink/status/1157402358394920960 - // It uses `CSSStyleSheetConstructor`, which is defined inside "static/src/polyfill.js", or - // falls back to constructible sheets on Chrome and friends. - const out = `const sheet = new (window.CSSStyleSheetConstructor || CSSStyleSheet)(); -sheet.replaceSync(${JSON.stringify(code)}); -export default sheet;`; - return { code: out, needsModuleRewrite: false }; - } - - case '.json': { - // differs from dataToEsm; spec says there's just one export - return { code: `export default ${code};`, needsModuleRewrite: false }; - } - - case '.html': { - const out = await htmlModules(code); - return { code: out, needsModuleRewrite: true }; - } - } - - return null; -} \ No newline at end of file diff --git a/build/transpile/base.js b/build/transpile/base.js deleted file mode 100644 index 9d563868..00000000 --- a/build/transpile/base.js +++ /dev/null @@ -1,135 +0,0 @@ -/** - * Copyright 2020 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @fileoverview Minimal version of Closure's base.js for Santa scene transpilation inside ES6 - * module contexts. - * - * This MUST be named `base.js` and contain the annotation below to be detected and used. It must - * also declare 'COMPILED' below. - * - * @provideGoog - */ - - -/** - * @define {boolean} Never changed by the compiler, as this is used for transpilation only. - */ -var COMPILED = false; - - -/** - * @param {string} v - * @return {boolean} whether the variable named v is defined in global scope - */ -function _check(v) { - try { - self.eval(v); - } catch (e) { - return false; - } - return true; -} - -var goog = goog || {}; - -/** - * @param {string} v - */ -goog.provide = function(v) { - var all = v.split('.'); - var run = ''; - if (!_check(all[0])) { - run += 'var ' + all[0] + '= {};'; - } - for (var i = 2; i < all.length+1; ++i) { - var part = all.slice(0, i).join('.'); - run += part + '=' + part + '||{};'; - } - self.eval(run); -}; - -/** - * @return {void} - */ -goog.require = function() {}; - -/** - * @param {function(): void} fn - */ -goog.scope = function(fn) { - fn(); -}; - - -/* - * nb. Fast transpiled helpers for ES6 features below this line. This is somewhat cribbed from - * Closure's internal `--rewrite_polyfills` feature, which does not seem to run in WHITESPACE_ONLY - * mode (and is sometimes too aggressive). - */ - - -var global = self; -var $jscomp = {global: global}; - -$jscomp.inherits = function(c, p) { - c.prototype = Object.create(p.prototype); - c.prototype.constructor = c; - Object.setPrototypeOf(c, p); -}; - -goog.inherits = $jscomp.inherits; - -/** - * @template T - * @param {!IArrayLike} array - * @return {function(): {done: boolean, value: T}} - */ -$jscomp.arrayIteratorImpl = function(array) { - let index = 0; - return function() { - if (index < array.length) { - return { - done: false, - value: array[index++], - }; - } else { - return {done: true}; - } - }; -}; - -/** - * @template T - * @param {!IArrayLike} array - * @return {!Iterator} - */ -$jscomp.arrayIterator = function(array) { - return /** @type {!Iterator} */ ({next: $jscomp.arrayIteratorImpl(array)}); -}; - -/** - * @template T - * @param {!IArrayLike} iterable - * @return {!Iterator} - */ -$jscomp.makeIterator = function(iterable) { - // NOTE: Disabling typechecking because [] not allowed on @struct. - var iteratorFunction = typeof Symbol != 'undefined' && Symbol.iterator && - (/** @type {?} */ (iterable)[Symbol.iterator]); - return iteratorFunction ? iteratorFunction.call(iterable) : - $jscomp.arrayIterator(/** @type {!Array} */ (iterable)); -}; diff --git a/build/transpile/export.js b/build/transpile/export.js deleted file mode 100644 index 75356084..00000000 --- a/build/transpile/export.js +++ /dev/null @@ -1,21 +0,0 @@ -/** - * Copyright 2020 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -goog.provide('_globalExportEntry'); -goog.require('app.Game'); - -/** @suppress {checkVars} */ -_globalExport = app.Game; \ No newline at end of file diff --git a/build/transpile/magic-externs.js b/build/transpile/magic-externs.js deleted file mode 100644 index 890ce663..00000000 --- a/build/transpile/magic-externs.js +++ /dev/null @@ -1,31 +0,0 @@ -/** - * Copyright 2020 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @externs For static/src/magic.js. - */ - -/** - * @param {string} id of message - * @return {string} resulting text of message - */ -function _msg(id) {} - -/** - * @param {string} path to join to static - * @return {string} resolved path - */ -function _static(path) {} diff --git a/build/writer.js b/build/writer.js deleted file mode 100644 index 56722491..00000000 --- a/build/writer.js +++ /dev/null @@ -1,125 +0,0 @@ -/** - * Copyright 2020 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -const path = require('path'); -const fs = require('fs').promises; - -class Writer { - constructor(options) { - options = Object.assign({ - loader: () => {}, - allowed: [], - target: '', - }, options); - - this._work = []; - this._output = {}; - - this._loader = options.loader; - this._allowed = options.allowed.slice(); - this._target = options.target; - } - - /** - * Validates that the passed directory is a valid output target. - * - * @param {string} input to process - * @return {string} normalized target - */ - _validate(input) { - const f = path.normalize(input); - const leftPart = f.split(path.sep, 1)[0]; - - if (this._allowed.length) { - if (this._allowed.indexOf(leftPart) === -1) { - throw new TypeError(`Disallowing write: ${f}`); - } - } - if (leftPart.startsWith('.')) { - throw new TypeError(`Disallowing dangerous write: ${f}`) - } - - return f; - } - - _add(name, fn) { - name = this._validate(name); - if (name in this._output) { - throw new Error(`Already written: ${name}`); - } - - const target = path.join(this._target, name); - const dir = path.dirname(target); - - const p = fs.mkdir(dir, {recursive: true}).then(() => target) - .then(fn) - .then(() => undefined); // clear result - - this._output[name] = p; - this._work.push(p); - - return p; - } - - /** - * Add a single file to the release queue. - * - * @param {string} name - * @param {(string|?Buffer)=} content to use, or read from disk - * @return {!Promise} - */ - file(name, content=null) { - return this._add(name, async (target) => { - // nb. explicitly uses ==, to find null and undefined - content = content == null ? await this._loader(name) : content; - return content == null ? fs.copyFile(name, target) : fs.writeFile(target, content); - }); - } - - /** - * Adds many files to the release queue. - * - * @param {!Array|!Object} all - * @return {!number} - */ - all(all) { - const work = []; - - if (Array.isArray(all)) { - const safe = new Set(all); // allow dups at the same time for copies - safe.forEach((name) => { - work.push(this.file(name)); - }); - } else { - for (const name in all) { - work.push(this.file(name, all[name])); - } - } - - return work.length; - } - - /** - * @return {!Promise} - */ - wait() { - return Promise.all(this._work).then((done) => done.length); - } -} - -module.exports = { - Writer, -}; diff --git a/docs/contributing.md b/docs/contributing.md deleted file mode 100644 index db0d1ce9..00000000 --- a/docs/contributing.md +++ /dev/null @@ -1,33 +0,0 @@ -# How to Contribute - -This repository contains the code to [Google Santa Tracker](https://santatracker.google.com), an educational and entertaining tradition for the December holiday period. - -We hope you find this source code interesting. -In general, we do not accept external contributions from the public. -You can file bug reports or feature requests, or contact the engineering lead [Jez Swanson](https://twitter.com/jezzamonn). - -(This text duplicated in [README.md](../README.md)) - -## Contributor License Agreement - -Contributions to this project must be accompanied by a Contributor License -Agreement. You (or your employer) retain the copyright to your contribution; -this simply gives us permission to use and redistribute your contributions as -part of the project. Head over to to see -your current agreements on file or to sign a new one. - -You generally only need to submit a CLA once, so if you've already submitted one -(even if it was for a different project), you probably don't need to do it -again. - -## Code reviews - -All submissions, including submissions by project members, require review. We -use GitHub pull requests for this purpose. Consult -[GitHub Help](https://help.github.com/articles/about-pull-requests/) for more -information on using pull requests. - -## Community Guidelines - -This project follows [Google's Open Source Community -Guidelines](https://opensource.google/conduct/). diff --git a/index.html b/index.html new file mode 100644 index 00000000..71458aeb --- /dev/null +++ b/index.html @@ -0,0 +1,104 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + diff --git a/package.json b/package.json index 26f3e301..b6a4cf79 100644 --- a/package.json +++ b/package.json @@ -3,9 +3,9 @@ "version": "2021.1.0", "license": "Apache-2.0", "scripts": { + "_": "yarn vite static/scenes/penguindash-vite for just the game, whcih works", "vite": "vite --port 3000", "build": "vite build", - "dev": "npm run start", "start": "./serve.js", "release": "./release.js", @@ -49,6 +49,7 @@ "mocha": "^5.2.0", "mocha-headless-server": "^0.1.2", "parse5": "^5.1.0", + "phaser": "2.6.2", "polka": "^0.5.2", "pretty-ms": "^4.0.0", "regenerator-runtime": "^0.13.3", diff --git a/prod/index.html b/prod/index.html index b314b8e5..d988ad18 100644 --- a/prod/index.html +++ b/prod/index.html @@ -97,12 +97,13 @@ body { + -