diff --git a/.eslintignore b/.eslintignore index 5784a4b56..c6e4e5fcc 100644 --- a/.eslintignore +++ b/.eslintignore @@ -34,3 +34,6 @@ packages/now-node-bridge/bridge.* # now-static-build packages/now-static-build/test/fixtures + +# redwood +packages/redwood/test/fixtures diff --git a/packages/now-build-utils/src/fs/read-config-file.ts b/packages/now-build-utils/src/fs/read-config-file.ts index 5af63049c..f8d48b29c 100644 --- a/packages/now-build-utils/src/fs/read-config-file.ts +++ b/packages/now-build-utils/src/fs/read-config-file.ts @@ -15,7 +15,9 @@ async function readFileOrNull(file: string) { return null; } -export async function readConfigFile(files: string | string[]) { +export async function readConfigFile( + files: string | string[] +): Promise { files = Array.isArray(files) ? files : [files]; for (const name of files) { @@ -24,11 +26,11 @@ export async function readConfigFile(files: string | string[]) { if (data) { const str = data.toString('utf8'); if (name.endsWith('.json')) { - return JSON.parse(str); + return JSON.parse(str) as T; } else if (name.endsWith('.toml')) { return (toml.parse(str) as unknown) as T; } else if (name.endsWith('.yaml') || name.endsWith('.yml')) { - return yaml.safeLoad(str, { filename: name }); + return yaml.safeLoad(str, { filename: name }) as T; } } } diff --git a/packages/now-cli/package.json b/packages/now-cli/package.json index 1f0c52e46..26ecb62b8 100644 --- a/packages/now-cli/package.json +++ b/packages/now-cli/package.json @@ -67,6 +67,7 @@ "@vercel/next": "2.6.14-canary.1", "@vercel/node": "1.7.4-canary.0", "@vercel/python": "1.2.2", + "@vercel/redwood": "0.0.1", "@vercel/ruby": "1.2.3", "@vercel/static-build": "0.17.7-canary.0", "update-notifier": "4.1.0" diff --git a/packages/now-static-build/src/frameworks.ts b/packages/now-static-build/src/frameworks.ts index fab633787..d36b44c71 100644 --- a/packages/now-static-build/src/frameworks.ts +++ b/packages/now-static-build/src/frameworks.ts @@ -542,7 +542,8 @@ const frameworkList: Framework[] = [ slug: 'hugo', buildCommand: 'hugo -D --gc', getOutputDirName: async (dirPrefix: string): Promise => { - const config = await readConfigFile( + type HugoConfig = { publishDir?: string }; + const config = await readConfigFile( ['config.json', 'config.yaml', 'config.toml'].map(fileName => { return join(dirPrefix, fileName); }) @@ -556,7 +557,10 @@ const frameworkList: Framework[] = [ slug: 'jekyll', buildCommand: 'jekyll build', getOutputDirName: async (dirPrefix: string): Promise => { - const config = await readConfigFile(join(dirPrefix, '_config.yml')); + type JekyllConfig = { destination?: string }; + const config = await readConfigFile( + join(dirPrefix, '_config.yml') + ); return (config && config.destination) || '_site'; }, }, diff --git a/packages/redwood/.gitignore b/packages/redwood/.gitignore new file mode 100644 index 000000000..e3ead05cb --- /dev/null +++ b/packages/redwood/.gitignore @@ -0,0 +1,3 @@ +/dist +/src/bridge.ts +/src/launcher.ts diff --git a/packages/redwood/build.sh b/packages/redwood/build.sh new file mode 100755 index 000000000..f4918b21b --- /dev/null +++ b/packages/redwood/build.sh @@ -0,0 +1,18 @@ +#!/bin/bash +set -euo pipefail + +# Copy shared dependencies +bridge_defs="$(dirname $(pwd))/now-node-bridge/src/bridge.ts" +launcher_defs="$(dirname $(pwd))/now-node/src/launcher.ts" + +cp -v "$bridge_defs" src +cp -v "$launcher_defs" src + +# Start fresh +rm -rf dist + +## Build ts files +tsc + +# Build with `ncc` +#ncc build src/index.ts -e @vercel/build-utils -e @now/build-utils -o dist diff --git a/packages/redwood/package.json b/packages/redwood/package.json new file mode 100644 index 000000000..e249ecdb9 --- /dev/null +++ b/packages/redwood/package.json @@ -0,0 +1,29 @@ +{ + "name": "@vercel/redwood", + "version": "0.0.1", + "main": "./dist/index.js", + "license": "MIT", + "homepage": "https://vercel.com/docs", + "files": [ + "dist" + ], + "repository": { + "type": "git", + "url": "https://github.com/vercel/vercel.git", + "directory": "packages/redwood" + }, + "scripts": { + "build": "./build.sh", + "test-integration-once": "jest --env node --verbose --runInBand --bail", + "prepublishOnly": "./build.sh" + }, + "dependencies": { + "@netlify/zip-it-and-ship-it": "1.2.0", + "is-port-reachable": "3.0.0" + }, + "devDependencies": { + "@types/aws-lambda": "8.10.19", + "@types/node": "*", + "typescript": "3.9.3" + } +} diff --git a/packages/redwood/src/index.ts b/packages/redwood/src/index.ts new file mode 100644 index 000000000..4e0d36ce8 --- /dev/null +++ b/packages/redwood/src/index.ts @@ -0,0 +1,228 @@ +import { join, dirname, relative, parse as parsePath, sep } from 'path'; +import { ChildProcess, SpawnOptions } from 'child_process'; +import { + BuildOptions, + Lambda, + Files, + PrepareCacheOptions, + createLambda, + download, + glob, + debug, + getNodeVersion, + getSpawnOptions, + runNpmInstall, + execCommand, + spawnCommand, + readConfigFile, + FileBlob, + FileFsRef, + NowBuildError, +} from '@vercel/build-utils'; +import { makeAwsLauncher } from './launcher'; +const { + getDependencies, + // eslint-disable-next-line @typescript-eslint/no-var-requires +} = require('@netlify/zip-it-and-ship-it/src/dependencies.js'); +//@ts-ignore +import isPortReachable from 'is-port-reachable'; + +interface RedwoodConfig { + web?: { + port?: number; + apiProxyPath?: string; + }; + api?: { + port?: number; + }; + browser?: { + open?: boolean; + }; +} + +const LAUNCHER_FILENAME = '___vc_launcher'; +const BRIDGE_FILENAME = '___vc_bridge'; +const HELPERS_FILENAME = '___vc_helpers'; +const SOURCEMAP_SUPPORT_FILENAME = '__vc_sourcemap_support'; + +const entrypointToPort = new Map(); +const childProcesses = new Set(); +export const version = 2; + +const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms)); + +async function waitForPort(port: number): Promise { + for (let i = 0; i < 500; i++) { + if (await isPortReachable(port)) { + return true; + } + await sleep(100); + } + return false; +} + +export async function build({ + workPath, + files, + entrypoint, + meta = {}, + config = {}, +}: BuildOptions) { + await download(files, workPath, meta); + + const mountpoint = dirname(entrypoint); + const entrypointFsDirname = join(workPath, mountpoint); + const nodeVersion = await getNodeVersion( + entrypointFsDirname, + undefined, + config, + meta + ); + + const spawnOpts = getSpawnOptions(meta, nodeVersion); + await runNpmInstall( + entrypointFsDirname, + ['--prefer-offline'], + spawnOpts, + meta + ); + + const { + buildCommand = 'yarn rw db up --no-db-client --auto-approve && yarn rw build', + devCommand = 'yarn rw dev', + } = config; + + if (meta.isDev) { + const toml = await readConfigFile( + join(mountpoint, 'redwood.toml') + ); + const webPort = toml?.web?.port || 8910; + const apiPort = toml?.web?.port || 8911; + let devPort = entrypointToPort.get(entrypoint); + + if (typeof devPort === 'number') { + debug('`%s` server already running for %j', devCommand, entrypoint); + } else { + devPort = webPort; + entrypointToPort.set(entrypoint, devPort); + + const opts: SpawnOptions = { + cwd: mountpoint, + stdio: 'inherit', + env: { ...spawnOpts.env, PORT: String(devPort) }, + }; + + const child = spawnCommand(devCommand, opts); + child.on('exit', () => entrypointToPort.delete(entrypoint)); + childProcesses.add(child); + + const found = await waitForPort(devPort); + if (!found) { + throw new NowBuildError({ + code: 'REDWOOD_PORT_UNAVAILABLE', + message: `Failed to detect a server running on port ${devPort}`, + action: 'More Details', + link: + 'https://err.sh/vercel/vercel/now-static-build-failed-to-detect-a-server', + }); + } + + debug('Detected dev server for %j', entrypoint); + } + + let srcBase = mountpoint.replace(/^\.\/?/, ''); + + if (srcBase.length > 0) { + srcBase = `/${srcBase}`; + } + + return { + routes: [ + { + src: `${srcBase}/api/(.*)`, + dest: `http://localhost:${apiPort}/$1`, + }, + { + src: `${srcBase}/(.*)`, + dest: `http://localhost:${webPort}/$1`, + }, + ], + watch: [join(srcBase, '**/*')], + output: {}, + }; + } + + debug('Running build command...'); + await execCommand(buildCommand, { + ...spawnOpts, + cwd: workPath, + }); + + const apiDistPath = join(workPath, 'api', 'dist', 'functions'); + const webDistPath = join(workPath, 'web', 'dist'); + const lambdaOutputs: { [filePath: string]: Lambda } = {}; + const staticOutputs = await glob('**', webDistPath); + + // Each file in the `functions` dir will become a lambda + const functionFiles = await glob('*.js', apiDistPath); + + for (const [funcName, fileFsRef] of Object.entries(functionFiles)) { + const outputName = join('api', parsePath(funcName).name); // remove `.js` extension + const absEntrypoint = fileFsRef.fsPath; + const dependencies: string[] = await getDependencies( + absEntrypoint, + workPath + ); + const relativeEntrypoint = relative(workPath, absEntrypoint); + const awsLambdaHandler = getAWSLambdaHandler(relativeEntrypoint, 'handler'); + + const lambdaFiles: Files = { + [`${LAUNCHER_FILENAME}.js`]: new FileBlob({ + data: makeAwsLauncher({ + entrypointPath: `./${relativeEntrypoint}`, + bridgePath: `./${BRIDGE_FILENAME}`, + helpersPath: `./${HELPERS_FILENAME}`, + sourcemapSupportPath: `./${SOURCEMAP_SUPPORT_FILENAME}`, + shouldAddHelpers: false, + shouldAddSourcemapSupport: false, + awsLambdaHandler, + }), + }), + [`${BRIDGE_FILENAME}.js`]: new FileFsRef({ + fsPath: join(__dirname, 'bridge.js'), + }), + }; + + dependencies.forEach(fsPath => { + lambdaFiles[relative(workPath, fsPath)] = new FileFsRef({ fsPath }); + }); + + lambdaFiles[relative(workPath, fileFsRef.fsPath)] = fileFsRef; + + const lambda = await createLambda({ + files: lambdaFiles, + handler: `${LAUNCHER_FILENAME}.launcher`, + runtime: nodeVersion.runtime, + environment: {}, + }); + lambdaOutputs[outputName] = lambda; + } + + return { + output: { ...staticOutputs, ...lambdaOutputs }, + routes: [{ handle: 'filesystem' }, { src: '/.*', dest: '/index.html' }], + watch: [], + }; +} + +function getAWSLambdaHandler(filePath: string, handlerName: string) { + const { dir, name } = parsePath(filePath); + return `${dir}${dir ? sep : ''}${name}.${handlerName}`; +} + +export async function prepareCache({ + workPath, +}: PrepareCacheOptions): Promise { + const cache = await glob('node_modules/**', workPath); + return cache; +} diff --git a/packages/redwood/test/fixtures/01-create-redwood-app/.env.defaults b/packages/redwood/test/fixtures/01-create-redwood-app/.env.defaults new file mode 100644 index 000000000..d4a59b75e --- /dev/null +++ b/packages/redwood/test/fixtures/01-create-redwood-app/.env.defaults @@ -0,0 +1,7 @@ +# These environment variables will be used by default if you do not create any +# yourself in .env. This file should be safe to check into your version control +# system. Any custom values should go in .env and .env should *not* be checked +# into version control. + +DATABASE_URL=file:./dev.db +BINARY_TARGET=native diff --git a/packages/redwood/test/fixtures/01-create-redwood-app/.gitignore b/packages/redwood/test/fixtures/01-create-redwood-app/.gitignore new file mode 100644 index 000000000..d38576738 --- /dev/null +++ b/packages/redwood/test/fixtures/01-create-redwood-app/.gitignore @@ -0,0 +1,10 @@ +.DS_Store +.env +.netlify +dev.db +dist +dist-babel +node_modules +yarn-error.log + +.vercel \ No newline at end of file diff --git a/packages/redwood/test/fixtures/01-create-redwood-app/.nvmrc b/packages/redwood/test/fixtures/01-create-redwood-app/.nvmrc new file mode 100644 index 000000000..b009dfb9d --- /dev/null +++ b/packages/redwood/test/fixtures/01-create-redwood-app/.nvmrc @@ -0,0 +1 @@ +lts/* diff --git a/packages/redwood/test/fixtures/01-create-redwood-app/LICENSE b/packages/redwood/test/fixtures/01-create-redwood-app/LICENSE new file mode 100644 index 000000000..f943bbba4 --- /dev/null +++ b/packages/redwood/test/fixtures/01-create-redwood-app/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Redwood + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/packages/redwood/test/fixtures/01-create-redwood-app/README.md b/packages/redwood/test/fixtures/01-create-redwood-app/README.md new file mode 100644 index 000000000..e950c6728 --- /dev/null +++ b/packages/redwood/test/fixtures/01-create-redwood-app/README.md @@ -0,0 +1,44 @@ +# Redwood + +> **HEADS UP:** RedwoodJS is _NOT_ ready for use in Production. It relies heavily on Prisma2, which is currently in testing with an expected production release coming soon. See status at ["Is Prisma2 Ready?"](https://isprisma2ready.com) + +## Getting Started + +- [Redwoodjs.com](https://redwoodjs.com): home to all things RedwoodJS. +- [Tutorial](https://redwoodjs.com/tutorial/welcome-to-redwood): getting started and complete overview guide. +- [Docs](https://redwoodjs.com/docs/introduction): using the Redwood Router, handling assets and files, list of command-line tools, and more. +- [Redwood Community](https://community.redwoodjs.com): get help, share tips and tricks, and collaborate on everything about RedwoodJS. + +### Setup + +We use Yarn as our package manager. To get the dependencies installed, just do this in the root directory: + +```terminal +yarn install +``` + +### Fire it up + +```terminal +yarn redwood dev +``` + +Your browser should open automatically to `http://localhost:8910` to see the web app. Lambda functions run on `http://localhost:8911` and are also proxied to `http://localhost:8910/api/functions/*`. + +## Development + +### Database + +We're using [Prisma2](https://github.com/prisma/prisma2), a modern DB toolkit to query, migrate and model your database. + +Prisma2 is [not ready for production](https://isprisma2ready.com) at the moment. + +To create a development database: + +```terminal +yarn redwood db up +``` + +This will read the schema definition in `api/prisma/schema.prisma` and generate a sqlite database in `api/prisma/dev.db` + +If you've made changes to the schema run `yarn redwood db save` to generate a migration, and `yarn redwood db up` to apply the migration/ generate a new ORM client. diff --git a/packages/redwood/test/fixtures/01-create-redwood-app/api/.babelrc.js b/packages/redwood/test/fixtures/01-create-redwood-app/api/.babelrc.js new file mode 100644 index 000000000..73439347d --- /dev/null +++ b/packages/redwood/test/fixtures/01-create-redwood-app/api/.babelrc.js @@ -0,0 +1 @@ +module.exports = { extends: '../babel.config.js' } diff --git a/packages/redwood/test/fixtures/01-create-redwood-app/api/jsconfig.json b/packages/redwood/test/fixtures/01-create-redwood-app/api/jsconfig.json new file mode 100644 index 000000000..eaa0ea153 --- /dev/null +++ b/packages/redwood/test/fixtures/01-create-redwood-app/api/jsconfig.json @@ -0,0 +1,9 @@ +{ + "compilerOptions": { + "baseUrl": ".", + "paths": { + "src/*": ["./src/*"] + } + }, + "include": ["src/**/*"] +} diff --git a/packages/redwood/test/fixtures/01-create-redwood-app/api/package.json b/packages/redwood/test/fixtures/01-create-redwood-app/api/package.json new file mode 100644 index 000000000..b760746de --- /dev/null +++ b/packages/redwood/test/fixtures/01-create-redwood-app/api/package.json @@ -0,0 +1,8 @@ +{ + "name": "api", + "version": "0.0.0", + "private": true, + "dependencies": { + "@redwoodjs/api": "0.14.0" + } +} diff --git a/packages/redwood/test/fixtures/01-create-redwood-app/api/prisma/schema.prisma b/packages/redwood/test/fixtures/01-create-redwood-app/api/prisma/schema.prisma new file mode 100644 index 000000000..415312cdc --- /dev/null +++ b/packages/redwood/test/fixtures/01-create-redwood-app/api/prisma/schema.prisma @@ -0,0 +1,18 @@ +datasource DS { + provider = "sqlite" + url = env("DATABASE_URL") +} + +generator client { + provider = "prisma-client-js" + binaryTargets = env("BINARY_TARGET") +} + +// Define your own datamodels here and run `yarn redwood db save` to create +// migrations for them. +// TODO: Please remove the following example: +model UserExample { + id Int @id @default(autoincrement()) + email String @unique + name String? +} diff --git a/packages/redwood/test/fixtures/01-create-redwood-app/api/prisma/seeds.js b/packages/redwood/test/fixtures/01-create-redwood-app/api/prisma/seeds.js new file mode 100644 index 000000000..ddc49863b --- /dev/null +++ b/packages/redwood/test/fixtures/01-create-redwood-app/api/prisma/seeds.js @@ -0,0 +1,26 @@ +/* eslint-disable no-console */ +const { PrismaClient } = require('@prisma/client') +const dotenv = require('dotenv') + +dotenv.config() +const db = new PrismaClient() + +async function main() { + // Seed data is database data that needs to exist for your app to run. + // Ideally this file should be idempotent: running it multiple times + // will result in the same database state (usually by checking for the + // existence of a record before trying to create it). For example: + // + // const existing = await db.user.findMany({ where: { email: 'admin@email.com' }}) + // if (!existing.length) { + // await db.user.create({ data: { name: 'Admin', email: 'admin@email.com' }}) + // } + + console.info('No data to seed. See api/prisma/seeds.js for info.') +} + +main() + .catch((e) => console.error(e)) + .finally(async () => { + await db.disconnect() + }) diff --git a/packages/redwood/test/fixtures/01-create-redwood-app/api/src/functions/graphql.js b/packages/redwood/test/fixtures/01-create-redwood-app/api/src/functions/graphql.js new file mode 100644 index 000000000..8f3661b82 --- /dev/null +++ b/packages/redwood/test/fixtures/01-create-redwood-app/api/src/functions/graphql.js @@ -0,0 +1,19 @@ +import { + createGraphQLHandler, + makeMergedSchema, + makeServices, +} from '@redwoodjs/api' +import importAll from '@redwoodjs/api/importAll.macro' + +import { db } from 'src/lib/db' + +const schemas = importAll('api', 'graphql') +const services = importAll('api', 'services') + +export const handler = createGraphQLHandler({ + schema: makeMergedSchema({ + schemas, + services: makeServices({ services }), + }), + db, +}) diff --git a/packages/redwood/test/fixtures/01-create-redwood-app/api/src/graphql/.keep b/packages/redwood/test/fixtures/01-create-redwood-app/api/src/graphql/.keep new file mode 100644 index 000000000..e69de29bb diff --git a/packages/redwood/test/fixtures/01-create-redwood-app/api/src/lib/db.js b/packages/redwood/test/fixtures/01-create-redwood-app/api/src/lib/db.js new file mode 100644 index 000000000..c33bf04d7 --- /dev/null +++ b/packages/redwood/test/fixtures/01-create-redwood-app/api/src/lib/db.js @@ -0,0 +1,6 @@ +// See https://github.com/prisma/prisma2/blob/master/docs/prisma-client-js/api.md#constructor +// for options. + +import { PrismaClient } from '@prisma/client' + +export const db = new PrismaClient() diff --git a/packages/redwood/test/fixtures/01-create-redwood-app/api/src/services/.keep b/packages/redwood/test/fixtures/01-create-redwood-app/api/src/services/.keep new file mode 100644 index 000000000..e69de29bb diff --git a/packages/redwood/test/fixtures/01-create-redwood-app/babel.config.js b/packages/redwood/test/fixtures/01-create-redwood-app/babel.config.js new file mode 100644 index 000000000..0bb758f7d --- /dev/null +++ b/packages/redwood/test/fixtures/01-create-redwood-app/babel.config.js @@ -0,0 +1,3 @@ +module.exports = { + presets: ['@redwoodjs/core/config/babel-preset'], +} diff --git a/packages/redwood/test/fixtures/01-create-redwood-app/netlify.toml b/packages/redwood/test/fixtures/01-create-redwood-app/netlify.toml new file mode 100644 index 000000000..011df1b0a --- /dev/null +++ b/packages/redwood/test/fixtures/01-create-redwood-app/netlify.toml @@ -0,0 +1,17 @@ +[build] + command = "yarn rw db up --no-db-client --auto-approve && yarn rw build" + publish = "web/dist" + functions = "api/dist/functions" + +[dev] + command = "yarn rw dev" + +[[redirects]] + from = "/*" + to = "/index.html" + status = 200 + +[[plugins]] +package = 'netlify-plugin-prisma-provider' + [plugins.inputs] + path = 'api/prisma/schema.prisma' diff --git a/packages/redwood/test/fixtures/01-create-redwood-app/package.json b/packages/redwood/test/fixtures/01-create-redwood-app/package.json new file mode 100644 index 000000000..37e26538f --- /dev/null +++ b/packages/redwood/test/fixtures/01-create-redwood-app/package.json @@ -0,0 +1,16 @@ +{ + "private": true, + "workspaces": { + "packages": [ + "api", + "web" + ] + }, + "devDependencies": { + "@redwoodjs/core": "0.14.0" + }, + "engines": { + "node": ">=12", + "yarn": ">=1.15" + } +} diff --git a/packages/redwood/test/fixtures/01-create-redwood-app/prettier.config.js b/packages/redwood/test/fixtures/01-create-redwood-app/prettier.config.js new file mode 100644 index 000000000..dce3eb3ac --- /dev/null +++ b/packages/redwood/test/fixtures/01-create-redwood-app/prettier.config.js @@ -0,0 +1,9 @@ +// https://prettier.io/docs/en/options.html +module.exports = { + trailingComma: 'es5', + bracketSpacing: true, + tabWidth: 2, + semi: false, + singleQuote: true, + arrowParens: 'always', +} diff --git a/packages/redwood/test/fixtures/01-create-redwood-app/redwood.toml b/packages/redwood/test/fixtures/01-create-redwood-app/redwood.toml new file mode 100644 index 000000000..587c4a177 --- /dev/null +++ b/packages/redwood/test/fixtures/01-create-redwood-app/redwood.toml @@ -0,0 +1,7 @@ +[web] + port = 8910 + apiProxyPath = "/api" +[api] + port = 8911 +[browser] + open = false diff --git a/packages/redwood/test/fixtures/01-create-redwood-app/vercel.json b/packages/redwood/test/fixtures/01-create-redwood-app/vercel.json new file mode 100644 index 000000000..0913fe943 --- /dev/null +++ b/packages/redwood/test/fixtures/01-create-redwood-app/vercel.json @@ -0,0 +1,20 @@ +{ + "version": 2, + "builds": [{ "src": "package.json", "use": "@vercel/redwood" }], + "probes": [ + { "path": "/", "mustContain": "
" }, + { "path": "/about", "mustContain": "
" }, + { + "path": "/api/graphql", + "headers": { "Accept": "text/html" }, + "mustContain": "GraphQL Playground" + }, + { + "path": "/api/graphql", + "method": "POST", + "headers": { "Accept": "application/json" }, + "body": { "query": "{ redwood { version } }" }, + "mustContain": "0.14.0" + } + ] +} diff --git a/packages/redwood/test/fixtures/01-create-redwood-app/web/.babelrc.js b/packages/redwood/test/fixtures/01-create-redwood-app/web/.babelrc.js new file mode 100644 index 000000000..73439347d --- /dev/null +++ b/packages/redwood/test/fixtures/01-create-redwood-app/web/.babelrc.js @@ -0,0 +1 @@ +module.exports = { extends: '../babel.config.js' } diff --git a/packages/redwood/test/fixtures/01-create-redwood-app/web/jsconfig.json b/packages/redwood/test/fixtures/01-create-redwood-app/web/jsconfig.json new file mode 100644 index 000000000..61c98378e --- /dev/null +++ b/packages/redwood/test/fixtures/01-create-redwood-app/web/jsconfig.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "baseUrl": ".", + "paths": { + "src/*": ["./src/*"] + }, + "jsx": "preserve" + }, + "include": ["src/**/*"] +} diff --git a/packages/redwood/test/fixtures/01-create-redwood-app/web/package.json b/packages/redwood/test/fixtures/01-create-redwood-app/web/package.json new file mode 100644 index 000000000..ecf5bad2b --- /dev/null +++ b/packages/redwood/test/fixtures/01-create-redwood-app/web/package.json @@ -0,0 +1,15 @@ +{ + "name": "web", + "version": "0.0.0", + "private": true, + "browserslist": [ + "defaults" + ], + "dependencies": { + "@redwoodjs/router": "0.14.0", + "@redwoodjs/web": "0.14.0", + "prop-types": "^15.7.2", + "react": "^16.13.1", + "react-dom": "^16.13.1" + } +} diff --git a/packages/redwood/test/fixtures/01-create-redwood-app/web/public/README.md b/packages/redwood/test/fixtures/01-create-redwood-app/web/public/README.md new file mode 100644 index 000000000..896833e2d --- /dev/null +++ b/packages/redwood/test/fixtures/01-create-redwood-app/web/public/README.md @@ -0,0 +1,45 @@ +# Static Assets + +Use this folder to add static files directly to your app. All included files and folders will be copied directly into the `/dist` folder (created when Webpack builds for production). They will also be available during development when you run `yarn rw dev`. + +> Note: files will _not_ hot reload while the development server is running. You'll need to manually stop/start to access file changes. + +### Example Use + +A file like `favicon.png` will be copied to `/dist/favicon.png`. A folder containing a file such as `static-files/my-logo.jpg` will be copied to `/dist/static-files/my-logo.jpg`. These can be referenced in your code directly without any special handling, e.g. + +``` + +``` + +and + +``` + alt="Logo" /> +``` + +Behind the scenes, we are using Webpack's ["copy-webpack-plugin"](https://github.com/webpack-contrib/copy-webpack-plugin). + +## Best Practices + +Because assets in this folder are bypassing the javascript module system, **this folder should be used sparingly** for assets such as favicons, robots.txt, manifests, libraries incompatible with Webpack, etc. + +In general, it's best to import files directly into a template, page, or component. This allows Webpack to include that file in the bundle, which ensures Webpack will correctly process and move assets into the distribution folder, providing error checks and correct paths along the way. + +### Example Asset Import with Webpack + +Instead of handling our logo image as a static file per the example above, we can do the following: + +``` +import React from "react" +import logo from "./my-logo.jpg" + + +function Header() { + return Logo +} + +export default Header +``` + +Behind the scenes, we are using Webpack's ["file-loader"](https://webpack.js.org/loaders/file-loader/) and ["url-loader](https://webpack.js.org/loaders/url-loader/) (for files smaller than 10kb). diff --git a/packages/redwood/test/fixtures/01-create-redwood-app/web/public/favicon.png b/packages/redwood/test/fixtures/01-create-redwood-app/web/public/favicon.png new file mode 100644 index 000000000..f0cdd00c8 Binary files /dev/null and b/packages/redwood/test/fixtures/01-create-redwood-app/web/public/favicon.png differ diff --git a/packages/redwood/test/fixtures/01-create-redwood-app/web/public/robots.txt b/packages/redwood/test/fixtures/01-create-redwood-app/web/public/robots.txt new file mode 100644 index 000000000..eb0536286 --- /dev/null +++ b/packages/redwood/test/fixtures/01-create-redwood-app/web/public/robots.txt @@ -0,0 +1,2 @@ +User-agent: * +Disallow: diff --git a/packages/redwood/test/fixtures/01-create-redwood-app/web/src/Routes.js b/packages/redwood/test/fixtures/01-create-redwood-app/web/src/Routes.js new file mode 100644 index 000000000..57afdaa79 --- /dev/null +++ b/packages/redwood/test/fixtures/01-create-redwood-app/web/src/Routes.js @@ -0,0 +1,22 @@ +// In this file, all Page components from 'src/pages` are auto-imported. Nested +// directories are supported, and should be uppercase. Each subdirectory will be +// prepended onto the component name. +// +// Examples: +// +// 'src/pages/HomePage/HomePage.js' -> HomePage +// 'src/pages/Admin/BooksPage/BooksPage.js' -> AdminBooksPage + +import { Router, Route } from '@redwoodjs/router' + +const Routes = () => { + return ( + + + + + + ) +} + +export default Routes diff --git a/packages/redwood/test/fixtures/01-create-redwood-app/web/src/components/.keep b/packages/redwood/test/fixtures/01-create-redwood-app/web/src/components/.keep new file mode 100644 index 000000000..e69de29bb diff --git a/packages/redwood/test/fixtures/01-create-redwood-app/web/src/index.css b/packages/redwood/test/fixtures/01-create-redwood-app/web/src/index.css new file mode 100644 index 000000000..e69de29bb diff --git a/packages/redwood/test/fixtures/01-create-redwood-app/web/src/index.html b/packages/redwood/test/fixtures/01-create-redwood-app/web/src/index.html new file mode 100644 index 000000000..8f69230b3 --- /dev/null +++ b/packages/redwood/test/fixtures/01-create-redwood-app/web/src/index.html @@ -0,0 +1,12 @@ + + + + + + + <%= htmlWebpackPlugin.options.title %> + + +
+ + diff --git a/packages/redwood/test/fixtures/01-create-redwood-app/web/src/index.js b/packages/redwood/test/fixtures/01-create-redwood-app/web/src/index.js new file mode 100644 index 000000000..63dd948e3 --- /dev/null +++ b/packages/redwood/test/fixtures/01-create-redwood-app/web/src/index.js @@ -0,0 +1,16 @@ +import ReactDOM from 'react-dom' +import { RedwoodProvider, FatalErrorBoundary } from '@redwoodjs/web' +import FatalErrorPage from 'src/pages/FatalErrorPage' + +import Routes from 'src/Routes' + +import './index.css' + +ReactDOM.render( + + + + + , + document.getElementById('redwood-app') +) diff --git a/packages/redwood/test/fixtures/01-create-redwood-app/web/src/layouts/.keep b/packages/redwood/test/fixtures/01-create-redwood-app/web/src/layouts/.keep new file mode 100644 index 000000000..e69de29bb diff --git a/packages/redwood/test/fixtures/01-create-redwood-app/web/src/pages/AboutPage/AboutPage.js b/packages/redwood/test/fixtures/01-create-redwood-app/web/src/pages/AboutPage/AboutPage.js new file mode 100644 index 000000000..9373c73ec --- /dev/null +++ b/packages/redwood/test/fixtures/01-create-redwood-app/web/src/pages/AboutPage/AboutPage.js @@ -0,0 +1,44 @@ +export default () => ( +
+