1
0
mirror of synced 2026-02-03 18:01:02 -05:00

Compare commits

...

100 Commits

Author SHA1 Message Date
Brandon Bayer
4184a4fe5d v0.0.1-0.0.1-canary.4.0 2020-04-14 16:47:37 +07:00
Brandon Bayer
5933b6189e chore: update lerna script 2020-04-14 16:46:55 +07:00
Brandon Bayer
2d545688cb feat(cli): don't bundle cli (and remove pkg) (#115)
* feat(cli): don't bundle cli with pkg or anything. npm package now working

* fix tests
2020-04-14 16:29:42 +07:00
Brandon Bayer
c3dee2271e feat: use db folder instead of prisma (#114)
* feat: use `db` folder instead of `prisma`

* fix tests
2020-04-14 14:58:44 +07:00
allcontributors[bot]
93865f4431 docs: add merelinguist as a contributor (#112)
* docs: update README.md [skip ci]

* docs: update .all-contributorsrc [skip ci]

Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
2020-04-14 12:43:45 +07:00
Dylan Brookes
be1c57b345 feat(server): Warning about invalid routes/api structure (#106)
* First pass at warning about duplicate routes

* Move dupe checker to its own function

* Fix some accidental changes

* Revert yarn.lock(?)

* Route > page

* Use pages for store example

* Warn about nested api routes

* Check for duplicate api routes

* Warn api dupe routes

* Remove stray process.exit

* Merge api and pages dupe checkers

* Delete old checkers

* Remove stray line

Co-authored-by: merelinguist <merelinguist@users.noreply.github.com>
Co-authored-by: Brandon Bayer <b@bayer.ws>
2020-04-14 12:43:01 +07:00
allcontributors[bot]
5d22f9b2cc docs: add wKovacs64 as a contributor (#111)
* docs: update README.md [skip ci]

* docs: update .all-contributorsrc [skip ci]

Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
2020-04-14 11:22:28 +07:00
Justin Hall
1ea4398216 fix(cli): spawn Windows .cmd entrypoints on win32 platform (#109)
Co-authored-by: Brandon Bayer <b@bayer.ws>
2020-04-14 11:21:30 +07:00
Rudi Yardley
bb93ed8843 fix(examples): Fix Yarn link blitz cli failing (#105)
* Add version to example

* Try using generic versions
2020-04-14 11:03:11 +07:00
Brandon Bayer
e2eed221e0 chore: update githead 2020-04-13 15:45:10 +07:00
Brandon Bayer
6719104cb3 v0.0.1-canary.1 2020-04-13 15:39:42 +07:00
Brandon Bayer
d103345b77 chore: remove version from examples 2020-04-13 15:38:54 +07:00
Brandon Bayer
ae99dc4a55 v0.0.1-canary.0 2020-04-13 15:35:08 +07:00
Brandon Bayer
4658f616f1 chore: update lerna config 2020-04-13 15:31:44 +07:00
Brandon Bayer
48861cbf25 chore: change version to 0.0.0 2020-04-13 15:27:16 +07:00
Brandon Bayer
c82de5cfd0 feat: Implement the new Architecture! (#95)
* wip

* working rpc mutation

* queries and mutations working!!

* prewarm the lamba

* update withBlitz config wrapper - remove stuff no longer needed

* switch from `pages` to `routes`

* fix a test issue

* fix cli test

* Add file addition and subtraction to transform stream

* Remove comments

* Allow pages and routes

* Allow routes folder without context

* Tidy up build rules

* Fix broken transform test

* Use done callback to ensure all events have passed in stream test

* Fix next build breaking

* File generation RPC works

* Remove commented out code

* Ensure correct jest version so tests run and tidy up

* Fix up linting

* Tidy up synchroniser

* Ensure watcher close is run when watchmode is false

* typo

* update cli readme

* Fix start error

* Downgrade ts-jest to remove warning

* fix build with static pages

* rerun prettier on all files

* yarn workspace: nohoist husky and prisma

* add missing dependency

Co-authored-by: Rudi Yardley <contact@rudiyardley.com>
2020-04-13 13:53:33 +07:00
Brandon Bayer
64185b884d chore: upgrade dependencies (#96) 2020-04-11 21:25:36 +07:00
Rudi Yardley
9bdc4350f9 fix(server): Fix ignorePath (#94)
* Fix up globs

* Revert move as it doesnt really matter because of ignorePaths
2020-04-11 14:17:25 +07:00
Rudi Yardley
81735c4dec Fix launchEditor bug (#92) 2020-04-11 16:37:39 +10:00
Brandon Bayer
53eab985fd chore: remove all prototype code in prep for new architecture code (#90)
* remove all prototype code and clean up deps

* fixes
2020-04-11 10:25:33 +07:00
Brandon Bayer
e16d66a4c5 docs: update readme 2020-04-09 10:20:49 +07:00
Dylan Brookes
bc3aa30929 docs: Fix website link (#86)
* Fix website link

* regenerate readme

Co-authored-by: Brandon Bayer <b@bayer.ws>
2020-04-07 18:18:01 +07:00
dependabot[bot]
9d8edb6ead chore(deps): bump acorn from 5.7.3 to 5.7.4 (#83)
Bumps [acorn](https://github.com/acornjs/acorn) from 5.7.3 to 5.7.4.
- [Release notes](https://github.com/acornjs/acorn/releases)
- [Commits](https://github.com/acornjs/acorn/compare/5.7.3...5.7.4)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Brandon Bayer <b@bayer.ws>
Co-authored-by: Rudi Yardley <contact@rudiyardley.com>
2020-04-07 14:47:54 +10:00
Brandon Bayer
25ff55291a chore: add rudi as code owner of packages/server 2020-04-07 09:18:33 +07:00
Rudi Yardley
2a9ac48a72 Fix dangling chokidar watch and add temporary logging. (#82)
* Add logging

* Add better commit message

* Ensure will only run if jest is running

* Add manifest to output log

* Update message

* Use specific ignore in build folder

* Add dot ahead of ignored patterns

* Close Watcher

* Remove stray reference

* Neaten up
2020-04-07 08:42:07 +07:00
Brandon Bayer
925098534f chore: update readme 2020-04-06 20:57:11 +07:00
Brandon Bayer
277c704be8 chore: tweak readme 2020-04-06 20:06:46 +07:00
Brandon Bayer
046b2ed300 Add @merelinguist as a contributor 2020-04-06 20:05:20 +07:00
Brandon Bayer
ef579daf1c chore: fix rishabhpoddar readme link 2020-04-06 20:05:13 +07:00
Lorenzo Rapetti
e44785bcff refactor(cli): CLI refactoring (#25)
* Add base generator class

* change from mem-fs to fs.mkdirSync due to undefined error

* Add base command class

* Add conflict checker

* Add dry run to generator

* Add install via npm or yarn

* Add --no-yarn

* remove unused nyc dependency

Co-authored-by: marcoseoane <marcorseoane@outlook.com>
Co-authored-by: Brandon Bayer <b@bayer.ws>
2020-04-06 18:27:29 +07:00
Brandon Bayer
72697f25f9 fix readme 2020-04-05 17:58:47 +07:00
allcontributors[bot]
fa6067eee7 docs: add marcoseoane as a contributor (#81)
* docs: update README.md [skip ci]

* docs: update .all-contributorsrc [skip ci]

Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
Co-authored-by: Brandon Bayer <b@bayer.ws>
2020-04-03 12:05:40 +07:00
allcontributors[bot]
6879be005e docs: add rishabhpoddar as a contributor (#78)
* docs: update README.md [skip ci]

* docs: update .all-contributorsrc [skip ci]

Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
Co-authored-by: Brandon Bayer <b@bayer.ws>
2020-04-03 12:04:22 +07:00
allcontributors[bot]
52e93a608b docs: add aem as a contributor (#79)
* docs: update README.md [skip ci]

* docs: update .all-contributorsrc [skip ci]

Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
Co-authored-by: Brandon Bayer <b@bayer.ws>
2020-04-03 12:02:10 +07:00
allcontributors[bot]
bce5a4bd37 docs: add lorenzorapetti as a contributor (#80)
* docs: update README.md [skip ci]

* docs: update .all-contributorsrc [skip ci]

Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
2020-04-03 11:59:58 +07:00
Brandon Bayer
1dad620368 doc: Add All Contributors Spec, Add CONTRIBUTING.md, Update Readme (#77)
* Added financial contributors to the README

* Add @flybayer as a contributor

* Add @medelman17 as a contributor

* Add @ryardley as a contributor

* Add @toddgeist as a contributor

* Add @quirk0o as a contributor

* Add @tsawan as a contributor

* Add @camilo86 as a contributor

* Add @dkempner as a contributor

* Add @gielcobben as a contributor

* Add @MrLeebo as a contributor

* Add @jimthedev as a contributor

* update readme & add CONTRIBUTING.md

* add Kristina

* Add @robdrosenberg as a contributor

* Add @jasonblalock as a contributor

* Add @coreybrown89 as a contributor

* Add @aej11a as a contributor

* update badegs

* tweak

* tweak

* more

Co-authored-by: Jess <jessachandler@gmail.com>
2020-04-03 11:40:31 +07:00
Brandon Bayer
f09b968e27 Update README.md 2020-04-02 20:14:26 +07:00
Brandon Bayer
7f81d6291d Revert "add initial file structure rfc"
This reverts commit f48a776e99.
2020-04-02 19:25:02 +07:00
Brandon Bayer
f48a776e99 add initial file structure rfc 2020-04-02 19:24:07 +07:00
Brandon Bayer
3e63287fa2 Update MANIFESTO.md 2020-04-01 13:13:21 +07:00
Beata Obrok
828c8d2f23 feat(cli): load dependencies in blitz console (#68)
* [cli] feat: autoload package dependencies in console

* feat: load Prisma Client into console context

* [cli] spec: fix spec for console command

Co-authored-by: Brandon Bayer <b@bayer.ws>
2020-03-25 12:13:55 +07:00
Rudi Yardley
d8c2f696b1 bug(server): CI pathname errors for readable-stream (#71)
* Try setting normalise to false on tree testing

* Remove debugging log
2020-03-24 12:57:35 +07:00
Rudi Yardley
4f21628365 feat(server): Transform rules and synchronisation manifest (#69)
* Extract config

* Use gulp pipes to apply tranforms and write manifest.

* Add linting comments to allow import reordering

* Fix manifest bug

* Refactor to pipeline and fix testing method

* Include clean

* Fix up dependencies and tidy up code

* Fix newline issue

* Fix dependencies again

* Fix up testing strategy for file manipulations

* Renaming file because of inconsistency pt1

* Renaming file because of inconsistency pt2

* Update CLI test

* Debug CI build failures not occuring locally

* Include catch for missing unlink
2020-03-20 15:45:24 +07:00
Brandon Bayer
0521b595fe chore: add github sponsor link 2020-03-13 14:16:51 +07:00
Brandon Bayer
f0159a05ae docs: update readme 2020-03-12 19:05:03 +07:00
Brandon Bayer
4a1e7e361f docs: add manifesto to readme 2020-03-11 22:00:20 +07:00
Rudi Yardley
b1f620a579 feat(server): Errors respect file path redirection (#66)
* Expose next-executable

* Redirecting errors correctly with mocked rules

* Add comments about what is waiting

* Remove prepare as it was causing errors when installing dependencies

* Mock outer function instead of call to spawn

* Try using import *

* Remove CI switch as that was not the problem

Co-authored-by: Brandon Bayer <b@bayer.ws>
2020-03-11 21:56:47 +07:00
Giel
dfbcb5bf67 feat: Upgrade next to latest release v9.3.0, closes #60 (#65)
Co-authored-by: Brandon Bayer <b@bayer.ws>
2020-03-11 21:45:54 +07:00
Brandon Bayer
0677a16e75 chore: tweak github actions to remove duplicate builds 2020-03-11 21:37:45 +07:00
Brandon Bayer
968b507570 fix: running tests locally after packages have been built 2020-03-11 17:57:15 +07:00
Rudi Yardley
e773b26f5f refactor(server): create server package (#57)
* @blitzjs/webpack -> @blitzjs/server

* Move server code out of cli

* Fix up type and lerna issues

* Prebuild non-cli before test

* Remove noise in PR
2020-03-11 14:02:23 +07:00
Brandon Bayer
54e4759791 docs: Add the Blitz Manifesto! (#52) 2020-03-10 18:06:40 +07:00
Tahir Awan
df0e5d3539 feat(cli): Add blitz db command #24 (#51)
* Add db command

* fixed test

closes #24

Co-authored-by: Brandon Bayer <b@bayer.ws>
2020-03-10 15:51:38 +07:00
Brandon Bayer
32944666f4 tweak funding 2020-03-10 10:48:32 +07:00
Tahir Awan
b5e0d7afed feat(cli): Add blitz test watch command #23 (#50)
* Add test watch

* Add test:watch

closes #23

Co-authored-by: Brandon Bayer <b@bayer.ws>
2020-03-09 15:30:08 +07:00
Rudi Yardley
4bb86dc8b8 fix(cli): Fix bug in build script (#49)
* Unlink doesnt work on folders

* Mock remove
2020-03-09 15:25:42 +07:00
Rudi Yardley
93a5fb057e feat(cli): Compile to Next (#45)
* Only create a prisma client when it actually needed

* Setup start script for a simple next-project

* Enable "compile to next" on first-demo

* Whitespace

* Fix failing webpack config test

* Testing spawn call

* Neaten up code

* Test tsExtraMock instead of reporter

* Test file deletion

* Better file locations

* Dont need persistent anymore

* Development, Production, Build, Deploy

* Add ts-ignore so we dont get compiler errors

* Remove test on command with flags

* Tidy up typescript config and add test

* Add comment around type issues

* Remove author field

* Tidy up config changs

* quiet core tests

* move es6 from root tsconfig to cli tsconfig

* Rename start -> scripts and tidy up descriptions

* Test build script

* Fix up test description

* Fix bug in build script

Closes #21, #36

Co-authored-by: Brandon Bayer <b@bayer.ws>
2020-03-09 09:37:59 +07:00
Beata Obrok
3c044fd4d0 test(examples): add cypress tests to first demo (#47)
* [examples] feat: setup cypress in first demo

* [examples] spec: add cypress tests for first demo

closes #44
2020-03-08 18:34:21 +07:00
Brandon Bayer
b430c87b65 fix(core): Add support for multiple attribute types for id (#35)
* Add support for multiple attribute types for id

* Add comment + tests
2020-03-05 21:05:22 +07:00
Brandon Bayer
903644b628 feat(core): Return 404 if controller action is not defined (#39)
Co-authored-by: Brandon Bayer <b@bayer.ws>
2020-03-05 11:11:39 +07:00
Brandon Bayer
d26be24cb3 feat(cli): use custom REPL in console command (#38)
Co-authored-by: Brandon Bayer <b@bayer.ws>
2020-03-05 10:51:06 +07:00
Brandon Bayer
949e7eb83f bug(webpack): Create webpack package to avoid context bugs (#42)
* Add new webpack package

* Delete webpack files from core

* Pin dependencies
2020-03-05 10:40:15 +07:00
Brandon Bayer
97bb455cc4 fix: Fix jest version incompatability (#43) 2020-03-05 08:12:35 +07:00
Brandon Bayer
cdb5ff2133 bug(example): Fix Invalid hook call error. (#37) 2020-03-05 08:11:13 +07:00
Brandon Bayer
b1aee93e2d chore: comment cleanup 2020-03-04 10:24:34 +07:00
Brandon Bayer
667566e341 docs: fix typo 2020-03-04 09:46:24 +07:00
Brandon Bayer
c51443bf5d docs: add dev onboarding blurb to readme 2020-03-04 09:45:26 +07:00
Camilo Gonzalez
ab4a3d2748 feat(cli): Add blitz test command (#30)
* add run tests command

* add test command

* add test cmd unit test

* uses project package manager (npm/yarn) to run test script

Co-authored-by: Brandon Bayer <b@bayer.ws>

closes #22
2020-03-04 09:37:46 +07:00
Beata Obrok
6d6a689557 feat(cli): Add blitz console command (#32)
Co-authored-by: Brandon Bayer <b@bayer.ws>
2020-03-04 09:36:22 +07:00
Brandon Bayer
16c2031d2a docs: Update CODE_OF_CONDUCT.md (#28) 2020-03-04 09:33:21 +07:00
Brandon Bayer
590b20f12e chore: add pull_request event to github action 2020-03-04 09:30:02 +07:00
Brandon Bayer
c82c0b3689 feat(core): Add withBlitz webpack wrapper + fix prisma/client (#31)
* add withBlitz webpack config wrapper

* Move prisma/client noop into withBlitz!
2020-03-03 16:16:10 +07:00
Jim Cummins
18d38d79e7 fix: noop @prisma/client in browser, closes #13 (#19)
Co-authored-by: Brandon Bayer <b@bayer.ws>
2020-03-02 21:50:56 +07:00
Jeremy Liberman
4113124ec4 chore(core): Add core tests (#16)
* Add core tests

- Tests cover Form and Controller
- You can check code coverage with "yarn test --coverage"

* A little formatting + form test modifications

* add back console log

Co-authored-by: Yanick Bélanger <yanick.belanger@yahoo.com>
Co-authored-by: Brandon Bayer <b@bayer.ws>
2020-03-02 10:22:08 +07:00
Brandon Bayer
8f6d0e03ac feat(cli): Add initial CLI (#8)
* v0.0.1

Initial PR for oclif CLI

* update prettier config

* chore: repo updates - run prettier on commit

* organize package.json + correct package name

* Add initial app generator

* changes & cleanup

* more tweaks

* Add basic nextjs generation

* #9 Swapping Mocha with Jest for CLI

Swapped Mocha with Jest.

* Swapping Mocha with Jest for CLI

Swapped Mocha with Jest.

* add `yarn b [COMMAND]` for dev convenience

* Formalize path arg

* Make jest work

* Remove empty commands and make tests pass

* Fix path argument

* Revert "target" in tsconfig.json

* Remove `target` from cli package.json

* Fix build command

* Use custom tsconfig for test script

* remove nyc because jest has coverage built in

* little cleanup

Co-authored-by: Mina Abadir <mina@abadir.email>
Co-authored-by: Lorenzo Rapetti <lorenzo.rapetti.94@gmail.com>
2020-03-01 15:46:35 +07:00
Brandon Bayer
c48fd8925b chore: add sponsor section to readme 2020-02-27 18:39:07 -08:00
Brandon Bayer
66af983955 chore: Add Patreon and Paypal to the sponsor button options (#18) 2020-02-27 18:36:11 -08:00
Brandon Bayer
55b735086c chore: add flybayer as codeowner of everything 2020-02-27 18:14:45 -08:00
Brandon Bayer
4e64784749 fix(demo): switch to fruit content filter 2020-02-27 18:13:49 -08:00
Brandon Bayer
3ee2ef0b42 Add Open Collective Sponsor button (#15) 2020-02-26 21:25:33 -08:00
Brandon Bayer
eaa6fc8802 fix(demo): add content filtering! 2020-02-26 17:11:56 -08:00
Brandon Bayer
ef6bf61c5b fix(demo): untitled posts 2020-02-26 16:33:55 -08:00
Brandon Bayer
5f5b589a7f chore(demo): add some comments 2020-02-26 16:07:18 -08:00
Brandon Bayer
82ae27841c Live Demo Deployment (#12)
* add github link to demo

* fix(core): connect to db right away + fix for connection undefined

* changes to demo needed for production deployment

* fix(demo): relax tsconfig

* upgrade prisma & fix core for deployment

* demo updates
2020-02-26 15:56:47 -08:00
Brandon Bayer
8623d5a817 Add initial Blitz Core and First Demo 🎉 (#11)
* wip

* chore: fix typings, tweak build config

* update prettier config

* set up husky to run prettier on commit

* add migrate script and update readme

* changes, still broken

* add yarn dev command

* more changes, still broken

* demo working again!

* fix: yarn why do you hate me so much?

* add to gitignore

* trying to get demo dev to work right

* refactor and cleanup

* more cleanup

* update deps

* tweak

* update readme

* fix

* update readme

Co-authored-by: Michael Edelman  <michael@edel.mn>
2020-02-26 12:46:30 -08:00
Brandon Bayer
250c49c7bd chore: update readme 2020-02-25 16:28:33 -08:00
Brandon Bayer
7739c3e951 remove broken gh action 2020-02-25 16:24:11 -08:00
Brandon Bayer
8a7f7931f4 Update README.md 2020-02-23 13:03:33 -08:00
Brandon Bayer
6579e85a96 docs: add code of conduct 2020-02-23 11:46:21 -08:00
Michael Edelman
1195f5225e chore: shush slack bot, add GH actions badge to README (#6) 2020-02-22 09:29:35 -08:00
Michael Edelman
9b206a9831 Implement Initial Continuous Integration Workflow Using GitHub Actions (#3)
* build: add GH action for CI

* fix: tweak test-related scripts in package.json
2020-02-20 14:12:36 -08:00
Michael Edelman
dae1db73e9 Create GitHub Action for Slack Notice 2020-02-20 16:07:12 -05:00
Michael Edelman
d63f59d2fa tada: bootstrap all the things (#2) 2020-02-19 16:33:26 -08:00
Brandon Bayer
bbf9cb0d2b Update README.md 2020-02-19 10:24:12 -08:00
Brandon Bayer
a44b1d93c1 Update README.md 2020-02-19 10:21:17 -08:00
Brandon Bayer
3dab930a75 Merge pull request #1 from dkempner/patch-1
Bigger click target = higher conversion rate
2020-02-17 14:51:14 -08:00
Daniel Kempner
939fad20f6 Bigger click target = higher conversion rate 2020-02-17 14:44:26 -08:00
Brandon Bayer
2e06ef8637 Update README.md 2020-02-17 14:28:53 -08:00
Brandon Bayer
12ab14bc57 Update README.md 2020-02-17 13:56:53 -08:00
Brandon Bayer
45000493e0 Update README.md 2020-02-17 13:55:13 -08:00
143 changed files with 20127 additions and 2 deletions

219
.all-contributorsrc Normal file
View File

@@ -0,0 +1,219 @@
{
"projectName": "blitz",
"projectOwner": "blitz-js",
"repoType": "github",
"repoHost": "https://github.com",
"files": [
"README.md"
],
"imageSize": 100,
"commit": true,
"commitConvention": "none",
"contributors": [
{
"login": "flybayer",
"name": "Brandon Bayer",
"avatar_url": "https://avatars3.githubusercontent.com/u/8813276?v=4",
"profile": "https://twitter.com/flybayer",
"contributions": [
"code",
"content",
"ideas",
"review"
]
},
{
"login": "medelman17",
"name": "Michael Edelman ",
"avatar_url": "https://avatars1.githubusercontent.com/u/14793389?v=4",
"profile": "https://fabulas.io",
"contributions": [
"infra"
]
},
{
"login": "merelinguist",
"name": "Dylan Brookes",
"avatar_url": "https://avatars3.githubusercontent.com/u/24858006?v=4",
"profile": "https://merelinguist.now.sh",
"contributions": [
"ideas",
"review",
"code"
]
},
{
"login": "ryardley",
"name": "Rudi Yardley",
"avatar_url": "https://avatars0.githubusercontent.com/u/1256409?v=4",
"profile": "https://medium.com/@ryardley",
"contributions": [
"code",
"ideas"
]
},
{
"login": "toddgeist",
"name": "Todd Geist",
"avatar_url": "https://avatars2.githubusercontent.com/u/316792?v=4",
"profile": "http://www.geistinteractive.com",
"contributions": [
"financial"
]
},
{
"login": "quirk0o",
"name": "Beata Obrok",
"avatar_url": "https://avatars3.githubusercontent.com/u/5123725?v=4",
"profile": "https://github.com/quirk0o",
"contributions": [
"code"
]
},
{
"login": "tsawan",
"name": "Tahir Awan",
"avatar_url": "https://avatars3.githubusercontent.com/u/3263082?v=4",
"profile": "https://github.com/tsawan",
"contributions": [
"code"
]
},
{
"login": "camilo86",
"name": "Camilo Gonzalez",
"avatar_url": "https://avatars1.githubusercontent.com/u/2454632?v=4",
"profile": "https://raluce.com",
"contributions": [
"code"
]
},
{
"login": "dkempner",
"name": "Daniel Kempner",
"avatar_url": "https://avatars3.githubusercontent.com/u/2532112?v=4",
"profile": "http://da.nielkempner.com",
"contributions": [
"code"
]
},
{
"login": "gielcobben",
"name": "Giel",
"avatar_url": "https://avatars0.githubusercontent.com/u/2663212?v=4",
"profile": "http://gielcobben.com",
"contributions": [
"code"
]
},
{
"login": "MrLeebo",
"name": "Jeremy Liberman",
"avatar_url": "https://avatars3.githubusercontent.com/u/2754163?v=4",
"profile": "http://jeremyliberman.com/",
"contributions": [
"code"
]
},
{
"login": "jimthedev",
"name": "Jim Cummins",
"avatar_url": "https://avatars0.githubusercontent.com/u/108938?v=4",
"profile": "https://jimthedev.com",
"contributions": [
"code"
]
},
{
"name": "Kristina Matuška",
"avatar_url": "https://media-exp1.licdn.com/dms/image/C5603AQHVPAjV21gw9g/profile-displayphoto-shrink_200_200/0?e=1591228800&v=beta&t=0MlbmiYhNvGv1xjLD_fOhOFjVDZ7ltNwfGNeJ4DHedQ",
"profile": "http://kristinamatuska.com/",
"contributions": [
"design"
]
},
{
"login": "robdrosenberg",
"name": "Robert Rosenberg",
"avatar_url": "https://avatars0.githubusercontent.com/u/20813991?v=4",
"profile": "http://robdrosenberg.com",
"contributions": [
"code"
]
},
{
"login": "jasonblalock",
"name": "Jason Blalock",
"avatar_url": "https://avatars0.githubusercontent.com/u/5899929?v=4",
"profile": "https://github.com/jasonblalock",
"contributions": [
"code"
]
},
{
"login": "coreybrown89",
"name": "Corey Brown",
"avatar_url": "https://avatars1.githubusercontent.com/u/12791148?v=4",
"profile": "https://corey-brown.com",
"contributions": [
"code"
]
},
{
"login": "aej11a",
"name": "aej11a",
"avatar_url": "https://avatars2.githubusercontent.com/u/10066422?v=4",
"profile": "https://github.com/aej11a",
"contributions": [
"code"
]
},
{
"login": "marcoseoane",
"name": "marcoseoane",
"avatar_url": "https://avatars0.githubusercontent.com/u/28088807?v=4",
"profile": "https://github.com/marcoseoane",
"contributions": [
"ideas"
]
},
{
"login": "rishabhpoddar",
"name": "Rishabh Poddar",
"avatar_url": "https://avatars2.githubusercontent.com/u/2976287?v=4",
"profile": "https://github.com/rishabhpoddar",
"contributions": [
"ideas"
]
},
{
"login": "aem",
"name": "Adam Markon",
"avatar_url": "https://avatars0.githubusercontent.com/u/1909883?v=4",
"profile": "https://github.com/aem",
"contributions": [
"ideas"
]
},
{
"login": "lorenzorapetti",
"name": "Lorenzo Rapetti",
"avatar_url": "https://avatars1.githubusercontent.com/u/2632174?v=4",
"profile": "https://github.com/lorenzorapetti",
"contributions": [
"code"
]
},
{
"login": "wKovacs64",
"name": "Justin Hall",
"avatar_url": "https://avatars1.githubusercontent.com/u/1288694?v=4",
"profile": "https://github.com/wKovacs64",
"contributions": [
"code"
]
}
],
"contributorsPerLine": 7,
"skipCi": true
}

5
.github/CODEOWNERS vendored Normal file
View File

@@ -0,0 +1,5 @@
# https://help.github.com/en/github/creating-cloning-and-archiving-repositories/about-code-owners
* @flybayer
packages/server/* @ryardley

4
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1,4 @@
github: blitz-js
custom: ['https://paypal.me/thebayers']
open_collective: blitzjs
patreon: flybayer

41
.github/workflows/main.yml vendored Normal file
View File

@@ -0,0 +1,41 @@
name: Continuous Integration
on:
push:
branches:
- master
- canary
pull_request:
branches:
- master
- canary
jobs:
build_and_test:
name: Build & Test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Use Node.js
uses: actions/setup-node@v1
with:
node-version: '12.16.1'
- name: Get yarn cache directory path
id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn cache dir)"
- name: Cache Node.js modules
id: yarn-cache
uses: actions/cache@v1
with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-
- name: Install dependencies
run: yarn install --frozen-lockfile --silent
env:
CI: true
- name: Test Blitz Packages
run: yarn test
env:
CI: true

17
.gitignore vendored Normal file
View File

@@ -0,0 +1,17 @@
.log
.DS_Store
.jest-*
lib
node_modules
reports
*.log
**/.env*
.nyc_output
**/coverage
.yarn
.yarnrc
tsconfig.tsbuildinfo
.blitz
.next
dist
.now

16
.npmignore Normal file
View File

@@ -0,0 +1,16 @@
.DS_Store
.prettierrc
.nyc_output
.travis.yml
coverage
coverage.lcov
bench
docs
src
examples
babel.config.js
test
CONTRIBUTING.md
CODE_OF_CONDUCT.md
*.ts
!*.d.ts

18
.prettierignore Normal file
View File

@@ -0,0 +1,18 @@
**/migrations/**
.blitz
.next
.log
.DS_Store
.jest-*
lib
node_modules
reports
*.log
**/.env*
.nyc_output
**/coverage
.yarn
.yarnrc
tsconfig.tsbuildinfo
dist
bin

60
CODE_OF_CONDUCT.md Normal file
View File

@@ -0,0 +1,60 @@
# The Blitz Community Code of Conduct
The Blitz core members take this CoC very serious. All members, contributors and volunteers in this community are required to act according to the following Code of Conduct to keep Blitz a positive, growing project and community and help us provide and ensure a safe environment for everyone.
The Blitz community
## When Something Happens
If you see a Code of Conduct violation, follow these steps:
1. Let the person know that what they did is not appropriate and ask them to stop and/or edit their message(s).
2. That person should immediately stop the behavior and correct the issue.
3. If this doesnt happen, or if youre uncomfortable speaking up, contact Brandon Bayer ([Twitter](https://twitter.com/flybayer) | [Email](mailto:b@bayer.ws)).
When reporting, please include any relevant details, links, screenshots, context, or other information that may be used to better understand and resolve the situation.
The core members will prioritize the well-being and comfort of the recipients of the violation over the comfort of the violator.
## What We Believe and How We Act
- We are committed to providing a friendly, safe and welcoming environment for everyone, regardless of age, body size, culture, ethnicity, gender expression, gender identity, level of experience, nationality, personal ability or disability, physical appearance, physical or mental difference, race, religion, set of skills, sexual orientation, socio-economic status, and subculture. We welcome people regardless of these or other attributes.
- We are better together. We are more alike than different.
- Our community is based on mutual respect, tolerance, and encouragement.
- We believe that a diverse community where people treat each other with respect is stronger, more vibrant and has more potential contributors and more sources for ideas. We aim for more diversity.
- We are kind, welcoming and courteous to everyone.
- Were respectful of others, their positions, their skills, their commitments and their efforts.
- Were attentive in our communications, whether in person or online, and were tactful when approaching differing views.
- We are aware that language shapes reality. Thus, we use inclusive, gender-neutral language in the documents we provide and when we talk to people. When referring to a group of people, we aim to use gender-neutral terms like “team”, “folks”, “everyone”. (For details, we recommend [this post](https://modelviewculture.com/pieces/gendered-language-feature-or-bug-in-software-documentation)).
- We respect that people have differences of opinion and criticize constructively.
- We value people over code.
## Don'ts
- Dont discriminate against anyone.
- Sexism and racism of any kind (including sexist and racist “jokes”), demeaning or insulting behaviour and harassment are seen as direct violations to this Code of Conduct. Harassment includes offensive verbal comments related to age, body size, culture, ethnicity, gender expression, gender identity, level of experience, nationality, personal ability or disability, physical appearance, physical or mental difference, race, religion, set of skills, sexual orientation, socio-economic status, and subculture. Harassment also includes sexual images in public spaces, deliberate intimidation, stalking, following, harassing photography or recording, inappropriate physical contact, and unwelcome sexual attention.
- On Slack and other online or offline communications channels, don't use overtly sexual nicknames or other nicknames that might detract from a friendly, safe and welcoming environment for all.
- Dont be mean or rude.
- Respect that some individuals and cultures consider the casual use of profanity offensive and off-putting.
- Unwelcome / non-consensual sexual advances over Slack or any other channels related with this community are not okay.
- Derailing, tone arguments and otherwise playing on peoples desires to be nice are not welcome, especially in discussions about violations to this Code of Conduct.
- Please avoid unstructured critique.
- Likewise any spamming, trolling, flaming, baiting or other attention-stealing behavior is not welcome.
- Sponsors of Blitz are also subject to this Code of Conduct. In particular, sponsors are required to not use sexualized images, activities, or other material which is not according to this Code of Conduct.
## Consequences for Violations to this Code of Conduct
If a participant engages in any behavior violating this Code of Conduct, the core members of this community will take any action they deem appropriate, starting with a gentle warning and then escalating as needed to expulsion from the community, exclusion from any interaction and loss of all rights in this community.
## Decisions About Consequences of Violations
Decisions about consequences of violations of this Code of Conduct are made by this communitys core members and may not be discussed with the person responsible for the violation.
## For Questions or Feedback
If you have any questions or feedback on this Code of Conduct, were happy to hear from you.
## Thanks for the Inspiration To
- [Hood.ie](http://hood.ie/code-of-conduct/)
- [WeAllJS](https://wealljs.org/code-of-conduct)

65
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,65 @@
# How to Contribute to Blitz.js
We're so excited you're interested in helping with Blitz! We happy to help you get started, even if you don't have any previous open-source experience :)
### First Things First
1. Familiarize yourself with the [Blitz Code of Conduct](https://github.com/blitz-js/blitz/blob/canary/CODE_OF_CONDUCT.md)
2. Join the [Blitz Slack Community](https://slack.blitzjs.com)
3. Install the [Zenhub browser extension](https://www.zenhub.com/extension)
4. View open issues and their progress [on the Zenhub repo tab](https://github.com/blitz-js/blitz#zenhub)
### What to Work On?
Issues with the label `ready to work on | help wanted` are the best place to start. If you find one that looks interesting and no one else is already working on it, comment in the issue that you are going to work on it. Please ask as many questions as you need, either directly in the issue or in Slack. We're happy to help!
After you contribute in any way, please add yourself as a contributor via the [@all-contributors bot](https://allcontributors.org/docs/en/bot/usage)!
## Development Setup
#### Repo Setup
**1.** Clone the repo
```
git clone git@github.com:blitz-js/blitz.git
cd blitz
```
**2.** Install dependencies
```
yarn
```
**3.** Start the package server. This must be running for any package development or example development
```
yarn dev
```
#### Develop a Blitz `package`
**1.** Change to a package directory
```
cd packages/core
```
**2.** Start the test runner
```
yarn test:watch
```
#### Run a Blitz `example`
**NOTE:** There are currently no examples for the new architecture in the pending RFC.
**1.** Change to an example directory
```
cd examples/first-demo
```
**2.** Follow instructions in the example's README

76
MANIFESTO.md Normal file
View File

@@ -0,0 +1,76 @@
# The Blitz.js Manifesto
## Background
Technology follows a repeating cycle of bundling and unbundling. Created in 2005, Ruby on Rails became a major bundling force. It made web application development easier and more accessible than ever before. This benefited everyone, from those learning programming to seniors building production systems.
A major unbundling happened in 2013 with the release of React because it is hyper focused on the rendering layer. As React grew in popularity, so did the choices for all the other parts, leaving developers with hundreds of decisions to make when starting a new app. While this has contributed to JavaScript Fatigue, it's been a powerful driving force for rapid frontend innovation.
Now, in 2020, is the perfect time for another major bundling. Developers are yearning for an easier, simpler way to build web applications. Beginners want a guiding hand for building a robust app. And seniors want a framework that removes mundane tasks and provides a highly scalable architecture.
Hence the creation of Blitz.
## What is Blitz For?
Blitz is for building tiny to large fullstack database-backed applications that have one or more graphical user interfaces like web or mobile apps.
## Foundational Principles
1. Fullstack & Monolithic
2. API Not Required
3. Convention over Configuration
4. Loose Opinions
5. Easy to Start, Easy to Scale
6. Stability
7. Community over Code
### 1. Fullstack & Monolithic
A fullstack, monolithic application is simpler than an application where frontend and backend are developed and deployed separately. Monolithic doesn't mean it will be slow or hard to scale to large teams. Monolithic doesn't mean there isn't separation of concerns. Monolithic means you can reason about your app as a single entity.
### 2. API Not Required
Until now, choosing React for your view layer required you to have a REST or GraphQL API even if it wasn't used by third-parties. This additional complexity is a significant drawback not shared by traditional server rendered apps like Ruby on Rails.
Contrary to popular opinion, most apps don't need an API! If you are building a unicorn startup, then yes you'll need an API at some point. But unicorn startups are 1% of all applications. Most applications are much smaller, faithfully serving a constrained set of use cases for small to medium businesses that don't need Internet Scale.
This is akin to the microservices vs monolith debate. Huge companies are absolutely going to need a more microservice oriented architecture, but the vast majority of apps are much better served by a single monolith.
### 3. Convention over Configuration
Starting a new fullstack React app is currently too hard. You have to spend days on things like configuring eslint, prettier, husky, jest, cypress, typescript, deciding on a file structure, setting up a database, adding authentication and authorization, setting up a router, defining routing conventions, and setting up your styling library.
Blitz will make as many decisions and do as much work for you as possible. This makes it lightning fast to start real development. It also greatly benefits the community. Common project structure and architectural patterns make it easy to move from Blitz app to Blitz app and immediately feel at home.
Convention over configuration doesn't mean no configuration. It means configuration is optional. Blitz will provide all the escape hatches you need for bespoke customization.
### 4. Loose Opinions
Blitz is opinionated. The out-of-the-box experience guides you on a path perfect for most applications. However, Blitz isn't arrogant. It understands there are very good reasons for deviating from convention, and it allows you to do so. For example, Blitz has a conventional file structure, but, with few exceptions, doesn't _enforce_ it.
And when there's not community consensus, `blitz new` will prompt you to choose.
### 5. Easy to Start, Easy to Scale
A framework that's only easy for one end of an application lifecycle is not a good framework. Both starting and scaling must be easy.
Easy to start includes being easy for beginners and being easy to migrate existing Next.js apps to Blitz.
Scaling is important in all forms: lines of code, number of people working in the codebase, and code execution.
### 6. Stability
In the fast-paced world of Javascript, a stable, predictable release cycle is a breath of fresh air. A stable release cycle ensures minimal breaking changes, and it ensures you know exactly what and when a breaking change will occur. It also minimizes bugs in stable releases by ensuring features are in beta for a minimum amount of time. [Ember is the model citizen](https://emberjs.com/releases/) in this regard.
The exact details of the Blitz release cycle are to be determined, but we'll follow a pattern similar to Ember which strictly follows SemVer with stable releases every 6 weeks and LTS releases every 6 months.
Blitz will follow a public RFC (request for comments) process so all users and companies can participate in proposing and evaluating new features.
If a Blitz API needs to be removed, it will be deprecated in a minor release. Major releases will simply remove APIs already deprecated in a previous release.
### 7. Community over Code
The Blitz community is the most important aspect of the framework, by far.
We have a comprehensive [Code of Conduct](https://github.com/blitz-js/blitz/blob/canary/CODE_OF_CONDUCT.md). LGBTQ+, women, and minorities are especially welcome.
We are all in this together, from the youngest to the oldest. We are all more similar than we are different. We can and should solve problems together. We should learn from other communities, not compete against them.

100
README.md
View File

@@ -1,2 +1,98 @@
# blitz
Framework for building monolithic, full-stack, serverless React apps with zero data-fetching and zero client-side state management
# Blitz.js ⚡️
<!-- prettier-ignore-start -->
[![All Contributors](https://img.shields.io/badge/all_contributors-17-orange.svg?style=flat-square)](#contributors-)
[![Build Status](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Factions-badge.atrox.dev%2Fblitz-js%2Fblitz%2Fbadge%3Fref%3Dcanary&style=flat-square)](https://actions-badge.atrox.dev/blitz-js/blitz/goto?ref=canary)
<!-- prettier-ignore-end -->
Blitz is a Rails-like framework for monolithic, full-stack React apps without an API.
The central thesis is that most apps dont need a REST or GraphQL API. Blitz brings back the simplicity of server rendered frameworks like Ruby on Rails while preserving everything we love about React.
Additionally, Blitz is bringing other Rails goodness thats missing in the React ecosystem like file structure and routing conventions, a really nice console REPL, intelligent code-scaffolding, and a fine-tuned out-of-the-box setup with Prettier, Typescript, ESlint, Jest, Cypress, etc.
### What is Blitz Designed For?
Blitz is designed for tiny to large database-backed applications that have one or more graphical user interfaces.
Web support will be released first, followed by React Native. We are pursuing the dream of a single monolithic application that runs on web and mobile with maximum code sharing and minimal boilerplate.
### What are the Foundational Principles?
1. Fullstack & Monolithic
2. API Not Required
3. Convention over Configuration
4. Loose Opinions
5. Easy to Start, Easy to Scale
6. Stability
7. Community over Code
[The Blitz Manifesto](https://github.com/blitz-js/blitz/blob/canary/MANIFESTO.md) explains these principles in detail.
### 👉 [View the Architecture RFC](https://github.com/blitz-js/blitz/pull/73) for exact details on what a Blitz app looks like 👈
---
## Welcome to the Blitz Community 👋
The Blitz community is warm, safe, diverse, inclusive, and fun! LGBTQ+, women, and minorities are especially welcome. Please read our [Code of Conduct](https://github.com/blitz-js/blitz/blob/canary/CODE_OF_CONDUCT.md).
### You are invited to join us — lets build the future of web dev together! 🤝
1. [Join the Blitz Slack Community](https://slack.blitzjs.com)
2. If you're interested in helping, read [The Contributing Guide](CONTRIBUTING.md)
## Sponsors and Donations
- Contribute via [GitHub Sponsors](https://github.com/sponsors/blitz-js)
- Contribute via [PayPal](https://paypal.me/thebayers)
- Contribute via [Open Collective](https://opencollective.com/blitzjs)
- Contribute via [Patreon](https://patreon.com/flybayer)
_Sponsor Blitz and display your logo and hiring status here. This is a great way to get in front of early adopters! [See options on Open Collective](https://opencollective.com/blitzjs)_
## Contributors ✨
Thanks to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
<!-- prettier-ignore-start -->
<!-- markdownlint-disable -->
<table>
<tr>
<td align="center"><a href="https://twitter.com/flybayer"><img src="https://avatars3.githubusercontent.com/u/8813276?v=4" width="100px;" alt=""/><br /><sub><b>Brandon Bayer</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=flybayer" title="Code">💻</a> <a href="#content-flybayer" title="Content">🖋</a> <a href="#ideas-flybayer" title="Ideas, Planning, & Feedback">🤔</a> <a href="https://github.com/blitz-js/blitz/pulls?q=is%3Apr+reviewed-by%3Aflybayer" title="Reviewed Pull Requests">👀</a></td>
<td align="center"><a href="https://fabulas.io"><img src="https://avatars1.githubusercontent.com/u/14793389?v=4" width="100px;" alt=""/><br /><sub><b>Michael Edelman </b></sub></a><br /><a href="#infra-medelman17" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a></td>
<td align="center"><a href="https://merelinguist.now.sh"><img src="https://avatars3.githubusercontent.com/u/24858006?v=4" width="100px;" alt=""/><br /><sub><b>Dylan Brookes</b></sub></a><br /><a href="#ideas-merelinguist" title="Ideas, Planning, & Feedback">🤔</a> <a href="https://github.com/blitz-js/blitz/pulls?q=is%3Apr+reviewed-by%3Amerelinguist" title="Reviewed Pull Requests">👀</a> <a href="https://github.com/blitz-js/blitz/commits?author=merelinguist" title="Code">💻</a></td>
<td align="center"><a href="https://medium.com/@ryardley"><img src="https://avatars0.githubusercontent.com/u/1256409?v=4" width="100px;" alt=""/><br /><sub><b>Rudi Yardley</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=ryardley" title="Code">💻</a> <a href="#ideas-ryardley" title="Ideas, Planning, & Feedback">🤔</a></td>
<td align="center"><a href="http://www.geistinteractive.com"><img src="https://avatars2.githubusercontent.com/u/316792?v=4" width="100px;" alt=""/><br /><sub><b>Todd Geist</b></sub></a><br /><a href="#financial-toddgeist" title="Financial">💵</a></td>
<td align="center"><a href="https://github.com/quirk0o"><img src="https://avatars3.githubusercontent.com/u/5123725?v=4" width="100px;" alt=""/><br /><sub><b>Beata Obrok</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=quirk0o" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/tsawan"><img src="https://avatars3.githubusercontent.com/u/3263082?v=4" width="100px;" alt=""/><br /><sub><b>Tahir Awan</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=tsawan" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="https://raluce.com"><img src="https://avatars1.githubusercontent.com/u/2454632?v=4" width="100px;" alt=""/><br /><sub><b>Camilo Gonzalez</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=camilo86" title="Code">💻</a></td>
<td align="center"><a href="http://da.nielkempner.com"><img src="https://avatars3.githubusercontent.com/u/2532112?v=4" width="100px;" alt=""/><br /><sub><b>Daniel Kempner</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=dkempner" title="Code">💻</a></td>
<td align="center"><a href="http://gielcobben.com"><img src="https://avatars0.githubusercontent.com/u/2663212?v=4" width="100px;" alt=""/><br /><sub><b>Giel</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=gielcobben" title="Code">💻</a></td>
<td align="center"><a href="http://jeremyliberman.com/"><img src="https://avatars3.githubusercontent.com/u/2754163?v=4" width="100px;" alt=""/><br /><sub><b>Jeremy Liberman</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=MrLeebo" title="Code">💻</a></td>
<td align="center"><a href="https://jimthedev.com"><img src="https://avatars0.githubusercontent.com/u/108938?v=4" width="100px;" alt=""/><br /><sub><b>Jim Cummins</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=jimthedev" title="Code">💻</a></td>
<td align="center"><a href="http://kristinamatuska.com/"><img src="https://media-exp1.licdn.com/dms/image/C5603AQHVPAjV21gw9g/profile-displayphoto-shrink_200_200/0?e=1591228800&v=beta&t=0MlbmiYhNvGv1xjLD_fOhOFjVDZ7ltNwfGNeJ4DHedQ" width="100px;" alt=""/><br /><sub><b>Kristina Matuška</b></sub></a><br /><a href="#design" title="Design">🎨</a></td>
<td align="center"><a href="http://robdrosenberg.com"><img src="https://avatars0.githubusercontent.com/u/20813991?v=4" width="100px;" alt=""/><br /><sub><b>Robert Rosenberg</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=robdrosenberg" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/jasonblalock"><img src="https://avatars0.githubusercontent.com/u/5899929?v=4" width="100px;" alt=""/><br /><sub><b>Jason Blalock</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=jasonblalock" title="Code">💻</a></td>
<td align="center"><a href="https://corey-brown.com"><img src="https://avatars1.githubusercontent.com/u/12791148?v=4" width="100px;" alt=""/><br /><sub><b>Corey Brown</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=coreybrown89" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/aej11a"><img src="https://avatars2.githubusercontent.com/u/10066422?v=4" width="100px;" alt=""/><br /><sub><b>aej11a</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=aej11a" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/marcoseoane"><img src="https://avatars0.githubusercontent.com/u/28088807?v=4" width="100px;" alt=""/><br /><sub><b>marcoseoane</b></sub></a><br /><a href="#ideas-marcoseoane" title="Ideas, Planning, & Feedback">🤔</a></td>
<td align="center"><a href="https://github.com/rishabhpoddar"><img src="https://avatars2.githubusercontent.com/u/2976287?v=4" width="100px;" alt=""/><br /><sub><b>Rishabh Poddar</b></sub></a><br /><a href="#ideas-rishabhpoddar" title="Ideas, Planning, & Feedback">🤔</a></td>
<td align="center"><a href="https://github.com/aem"><img src="https://avatars0.githubusercontent.com/u/1909883?v=4" width="100px;" alt=""/><br /><sub><b>Adam Markon</b></sub></a><br /><a href="#ideas-aem" title="Ideas, Planning, & Feedback">🤔</a></td>
<td align="center"><a href="https://github.com/lorenzorapetti"><img src="https://avatars1.githubusercontent.com/u/2632174?v=4" width="100px;" alt=""/><br /><sub><b>Lorenzo Rapetti</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=lorenzorapetti" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/wKovacs64"><img src="https://avatars1.githubusercontent.com/u/1288694?v=4" width="100px;" alt=""/><br /><sub><b>Justin Hall</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=wKovacs64" title="Code">💻</a></td>
</tr>
</table>
<!-- markdownlint-enable -->
<!-- prettier-ignore-end -->
<!-- ALL-CONTRIBUTORS-LIST:END -->
This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!

3
__mocks__/fs.js Normal file
View File

@@ -0,0 +1,3 @@
const {fs} = require('memfs')
module.exports = fs

33
examples/store/.gitignore vendored Normal file
View File

@@ -0,0 +1,33 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# next.js
/.next/
/out/
/.blitz/
*.sqlite
# production
/build
.now
# misc
.DS_Store
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# local env files
.env.local
.env.development.local
.env.test.local
.env.production.local

15
examples/store/README.md Normal file
View File

@@ -0,0 +1,15 @@
## Getting Started
1. Setup your database
```
yarn blitz db migrate
```
2. Start the dev server
```
yarn blitz start
```
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.

View File

@@ -0,0 +1,16 @@
import {Link} from '@blitzjs/core'
export default function () {
return (
<div>
<h1>Store Admin</h1>
<div>
<p>
<Link href="/admin/products">
<a>Manage Products</a>
</Link>
</p>
</div>
</div>
)
}

View File

@@ -0,0 +1,28 @@
import {Link, useRouter, useQuery} from '@blitzjs/core'
import getProduct from 'app/products/queries/getProduct'
import ProductForm from 'app/products/components/ProductForm'
export default function () {
const router = useRouter()
const [product, {status, error}] = useQuery(getProduct, {where: {id: parseInt(router.query.id as string)}})
if (status === 'loading') {
return <div>Loading...</div>
} else if (status === 'error') {
return <div>Error: {error.message}</div>
}
return (
<div>
<h1>Edit Product</h1>
<div>
<ProductForm product={product} onSuccess={() => router.push('/admin/products')} />
</div>
<p>
<Link href="/admin">
<a>Store Admin</a>
</Link>
</p>
</div>
)
}

View File

@@ -0,0 +1,37 @@
import {useQuery, Link} from '@blitzjs/core'
import getProducts from 'app/products/queries/getProducts'
export default function () {
const [products, {status, error}] = useQuery(getProducts)
if (status === 'loading') {
return <div>Loading...</div>
} else if (status === 'error') {
return <div>Error: {error.message}</div>
}
return (
<div>
<h1>Products</h1>
<p>
<Link href="/admin/products/new">
<a>Create Product</a>
</Link>
<Link href="/admin">
<a style={{marginLeft: 16}}>Admin</a>
</Link>
</p>
<ul>
{products.map((product) => (
<li key={product.id}>
<Link href="/admin/products/[id]" as={`/admin/products/${product.id}`}>
<a>{product.name}</a>
</Link>
</li>
))}
</ul>
</div>
)
}

View File

@@ -0,0 +1,19 @@
import {Link, useRouter} from '@blitzjs/core'
import ProductForm from 'app/products/components/ProductForm'
export default function () {
const router = useRouter()
return (
<div>
<h1>Create a New Product</h1>
<div>
<ProductForm onSuccess={() => router.push('/admin/products')} />
</div>
<p>
<Link href="/admin">
<a>Store Admin</a>
</Link>
</p>
</div>
)
}

View File

@@ -0,0 +1,166 @@
import Head from 'next/head'
const Home = () => (
<div className="container">
<Head>
<title>Create Next App</title>
<link rel="icon" href="/favicon.ico" />
</Head>
<main>
<h1 className="title">About Store</h1>
</main>
<footer>
<a
href="https://zeit.co?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer">
Powered by <img src="/zeit.svg" alt="ZEIT Logo" />
</a>
</footer>
<style jsx>{`
.container {
min-height: 100vh;
padding: 0 0.5rem;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
main {
padding: 5rem 0;
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
footer {
width: 100%;
height: 100px;
border-top: 1px solid #eaeaea;
display: flex;
justify-content: center;
align-items: center;
}
footer img {
margin-left: 0.5rem;
}
footer a {
display: flex;
justify-content: center;
align-items: center;
}
a {
color: inherit;
text-decoration: none;
}
.title a {
color: #0070f3;
text-decoration: none;
}
.title a:hover,
.title a:focus,
.title a:active {
text-decoration: underline;
}
.title {
margin: 0;
line-height: 1.15;
font-size: 4rem;
}
.title,
.description {
text-align: center;
}
.description {
line-height: 1.5;
font-size: 1.5rem;
}
code {
background: #fafafa;
border-radius: 5px;
padding: 0.75rem;
font-size: 1.1rem;
font-family: Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono,
Bitstream Vera Sans Mono, Courier New, monospace;
}
.grid {
display: flex;
align-items: center;
justify-content: center;
flex-wrap: wrap;
max-width: 800px;
margin-top: 3rem;
}
.card {
margin: 1rem;
flex-basis: 45%;
padding: 1.5rem;
text-align: left;
color: inherit;
text-decoration: none;
border: 1px solid #eaeaea;
border-radius: 10px;
transition: color 0.15s ease, border-color 0.15s ease;
}
.card:hover,
.card:focus,
.card:active {
color: #0070f3;
border-color: #0070f3;
}
.card h3 {
margin: 0 0 1rem 0;
font-size: 1.5rem;
}
.card p {
margin: 0;
font-size: 1.25rem;
line-height: 1.5;
}
@media (max-width: 600px) {
.grid {
width: 100%;
flex-direction: column;
}
}
`}</style>
<style jsx global>{`
html,
body {
padding: 0;
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, Ubuntu, Cantarell, Fira Sans,
Droid Sans, Helvetica Neue, sans-serif;
}
* {
box-sizing: border-box;
}
`}</style>
</div>
)
export default Home

View File

@@ -0,0 +1,196 @@
import Head from 'next/head'
const Home = () => (
<div className="container">
<Head>
<title>Create Next App</title>
<link rel="icon" href="/favicon.ico" />
</Head>
<main>
<h1 className="title">
Welcome to <a href="https://nextjs.org">Next.js!</a>
</h1>
<p className="description">
Get started by editing <code>pages/index.js</code>
</p>
<div className="grid">
<a href="https://nextjs.org/docs" className="card">
<h3>Documentation &rarr;</h3>
<p>Find in-depth information about Next.js features and API.</p>
</a>
<a href="https://nextjs.org/learn" className="card">
<h3>Learn &rarr;</h3>
<p>Learn about Next.js in an interactive course with quizzes!</p>
</a>
<a href="https://github.com/zeit/next.js/tree/master/examples" className="card">
<h3>Examples &rarr;</h3>
<p>Discover and deploy boilerplate example Next.js projects.</p>
</a>
<a
href="https://zeit.co/import?filter=next.js&utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
className="card">
<h3>Deploy &rarr;</h3>
<p>Instantly deploy your Next.js site to a public URL with ZEIT Now.</p>
</a>
</div>
</main>
<footer>
<a
href="https://zeit.co?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer">
Powered by <img src="/zeit.svg" alt="ZEIT Logo" />
</a>
</footer>
<style jsx>{`
.container {
min-height: 100vh;
padding: 0 0.5rem;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
main {
padding: 5rem 0;
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
footer {
width: 100%;
height: 100px;
border-top: 1px solid #eaeaea;
display: flex;
justify-content: center;
align-items: center;
}
footer img {
margin-left: 0.5rem;
}
footer a {
display: flex;
justify-content: center;
align-items: center;
}
a {
color: inherit;
text-decoration: none;
}
.title a {
color: #0070f3;
text-decoration: none;
}
.title a:hover,
.title a:focus,
.title a:active {
text-decoration: underline;
}
.title {
margin: 0;
line-height: 1.15;
font-size: 4rem;
}
.title,
.description {
text-align: center;
}
.description {
line-height: 1.5;
font-size: 1.5rem;
}
code {
background: #fafafa;
border-radius: 5px;
padding: 0.75rem;
font-size: 1.1rem;
font-family: Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono,
Bitstream Vera Sans Mono, Courier New, monospace;
}
.grid {
display: flex;
align-items: center;
justify-content: center;
flex-wrap: wrap;
max-width: 800px;
margin-top: 3rem;
}
.card {
margin: 1rem;
flex-basis: 45%;
padding: 1.5rem;
text-align: left;
color: inherit;
text-decoration: none;
border: 1px solid #eaeaea;
border-radius: 10px;
transition: color 0.15s ease, border-color 0.15s ease;
}
.card:hover,
.card:focus,
.card:active {
color: #0070f3;
border-color: #0070f3;
}
.card h3 {
margin: 0 0 1rem 0;
font-size: 1.5rem;
}
.card p {
margin: 0;
font-size: 1.25rem;
line-height: 1.5;
}
@media (max-width: 600px) {
.grid {
width: 100%;
flex-direction: column;
}
}
`}</style>
<style jsx global>{`
html,
body {
padding: 0;
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, Ubuntu, Cantarell, Fira Sans,
Droid Sans, Helvetica Neue, sans-serif;
}
* {
box-sizing: border-box;
}
`}</style>
</div>
)
export default Home

View File

@@ -0,0 +1,74 @@
import {Form, Field} from 'react-final-form'
import {Product, ProductCreateInput, ProductUpdateInput} from 'db'
import createProduct from 'app/products/mutations/createProduct'
import updateProduct from 'app/products/mutations/updateProduct'
type ProductInput = ProductCreateInput | ProductUpdateInput
function isNew(product: ProductInput): product is ProductCreateInput {
return (product as ProductUpdateInput).id === undefined
}
type ProductFormProps = {
product?: ProductUpdateInput
style?: React.CSSProperties
onSuccess: (product: Product) => any
}
export default function ({product, style, onSuccess, ...props}: ProductFormProps) {
return (
<Form
initialValues={product || {name: null, handle: null, description: null, price: null}}
onSubmit={async (data: ProductInput) => {
if (isNew(data)) {
try {
const product = await createProduct({data})
onSuccess(product)
} catch (error) {
alert('Error creating product ' + JSON.stringify(error, null, 2))
}
} else {
try {
const product = await updateProduct({where: {id: data.id}, data})
onSuccess(product)
} catch (error) {
alert('Error updating product ' + JSON.stringify(error, null, 2))
}
}
}}
render={({handleSubmit}) => (
<form onSubmit={handleSubmit} style={{maxWidth: 400, ...style}} {...props}>
<div style={{marginBottom: 16}}>
<label style={{display: 'flex', flexDirection: 'column'}}>
Product Name
<Field name="name" component="input" />
</label>
</div>
<div style={{marginBottom: 16}}>
<label style={{display: 'flex', flexDirection: 'column'}}>
Handle
<Field name="handle" component="input" />
</label>
</div>
<div style={{marginBottom: 16}}>
<label style={{display: 'flex', flexDirection: 'column'}}>
Description
<Field name="description" component="textarea" />
</label>
</div>
<div style={{marginBottom: 16}}>
<label style={{display: 'flex', flexDirection: 'column'}}>
Price
<Field name="price" component="input" parse={(value) => (value ? parseInt(value) : null)} />
</label>
</div>
<button>{product ? 'Update' : 'Create'} Product</button>
</form>
)}
/>
)
}

View File

@@ -0,0 +1,7 @@
import db, {ProductCreateArgs} from 'db'
export default async function createProduct(args: ProductCreateArgs) {
const product = await db.product.create(args)
return product
}

View File

@@ -0,0 +1,7 @@
import db, {ProductDeleteArgs} from 'db'
export default async function deleteProduct(args: ProductDeleteArgs) {
const product = await db.product.delete(args)
return product
}

View File

@@ -0,0 +1,10 @@
import db, {ProductUpdateArgs} from 'db'
export default async function updateProduct(args: ProductUpdateArgs) {
// Don't allow updating ID
delete args.data.id
const product = await db.product.update(args)
return product
}

View File

@@ -0,0 +1,38 @@
import {Link, BlitzPage, GetStaticProps, GetStaticPaths} from '@blitzjs/core'
import getProduct from 'app/products/queries/getProduct'
import getProducts from 'app/products/queries/getProducts'
import {Product} from 'db'
type StaticProps = {
product: Product
}
export const getStaticProps: GetStaticProps<StaticProps> = async (ctx) => {
const product = await getProduct({where: {handle: ctx.params.handle as string}})
return {
props: {product},
}
}
export const getStaticPaths: GetStaticPaths = async () => {
const paths = (await getProducts()).map(({handle}) => ({params: {handle}}))
return {
paths,
fallback: false,
}
}
const Page: BlitzPage<StaticProps> = function ({product}) {
return (
<div>
<h1>{product.name}</h1>
<p>{product.description}</p>
<p>Price: ${product.price}</p>
<Link href="/products">
<a>All Products</a>
</Link>
</div>
)
}
export default Page

View File

@@ -0,0 +1,33 @@
import {Link, BlitzPage, GetStaticProps} from '@blitzjs/core'
import getProducts from 'app/products/queries/getProducts'
import {Product} from 'db'
type StaticProps = {
products: Product[]
}
export const getStaticProps: GetStaticProps<StaticProps> = async () => {
const products = await getProducts()
return {
props: {products},
}
}
const Page: BlitzPage<StaticProps> = function ({products}) {
return (
<div>
<h1>Products</h1>
<div>
{products.map((product) => (
<p key={product.id}>
<Link href="/products/[handle]" as={`/products/${product.handle}`}>
<a>{product.name}</a>
</Link>
</p>
))}
</div>
</div>
)
}
export default Page

View File

@@ -0,0 +1,7 @@
import db, {FindOneProductArgs} from 'db'
export default async function getProduct(args: FindOneProductArgs) {
const product = await db.product.findOne(args)
return product
}

View File

@@ -0,0 +1,7 @@
import db, {FindManyProductArgs} from 'db'
export default async function getProducts(args?: FindManyProductArgs) {
const products = await db.product.findMany(args)
return products
}

View File

@@ -0,0 +1,6 @@
import {PrismaClient} from '@prisma/client'
export * from '@prisma/client'
const prisma = new PrismaClient()
export default prisma

View File

@@ -0,0 +1,70 @@
# Migration `20200411130101-init`
This migration has been generated by Brandon Bayer at 4/11/2020, 1:01:01 PM.
You can check out the [state of the schema](./schema.prisma) after the migration.
## Database Steps
```sql
CREATE TABLE "quaint"."User" (
"email" TEXT NOT NULL ,
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
"name" TEXT ,
"role" TEXT ,
"storeId" INTEGER
)
CREATE TABLE "quaint"."Product" (
"description" TEXT ,
"handle" TEXT NOT NULL ,
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
"name" TEXT ,
"price" INTEGER
)
CREATE UNIQUE INDEX "quaint"."User.email" ON "User"("email")
CREATE UNIQUE INDEX "quaint"."Product.handle" ON "Product"("handle")
```
## Changes
```diff
diff --git schema.prisma schema.prisma
migration ..20200411130101-init
--- datamodel.dml
+++ datamodel.dml
@@ -1,0 +1,30 @@
+// This is your Prisma schema file,
+// learn more about it in the docs: https://pris.ly/d/prisma-schema
+
+datasource db {
+ provider = "sqlite"
+ url = "file:./db.sqlite"
+}
+
+generator client {
+ provider = "prisma-client-js"
+}
+
+
+// --------------------------------------
+
+model User {
+ id Int @default(autoincrement()) @id
+ email String @unique
+ name String?
+ role String?
+ storeId Int?
+}
+
+model Product {
+ id Int @default(autoincrement()) @id
+ handle String @unique
+ name String?
+ description String?
+ price Int?
+}
```

View File

@@ -0,0 +1,30 @@
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema
datasource db {
provider = "sqlite"
url = "***"
}
generator client {
provider = "prisma-client-js"
}
// --------------------------------------
model User {
id Int @default(autoincrement()) @id
email String @unique
name String?
role String?
storeId Int?
}
model Product {
id Int @default(autoincrement()) @id
handle String @unique
name String?
description String?
price Int?
}

View File

@@ -0,0 +1,199 @@
{
"version": "0.3.14-fixed",
"steps": [
{
"tag": "CreateSource",
"source": "db"
},
{
"tag": "CreateArgument",
"location": {
"tag": "Source",
"source": "db"
},
"argument": "provider",
"value": "\"sqlite\""
},
{
"tag": "CreateArgument",
"location": {
"tag": "Source",
"source": "db"
},
"argument": "url",
"value": "\"file:./db.sqlite\""
},
{
"tag": "CreateModel",
"model": "User"
},
{
"tag": "CreateField",
"model": "User",
"field": "id",
"type": "Int",
"arity": "Required"
},
{
"tag": "CreateDirective",
"location": {
"path": {
"tag": "Field",
"model": "User",
"field": "id"
},
"directive": "default"
}
},
{
"tag": "CreateArgument",
"location": {
"tag": "Directive",
"path": {
"tag": "Field",
"model": "User",
"field": "id"
},
"directive": "default"
},
"argument": "",
"value": "autoincrement()"
},
{
"tag": "CreateDirective",
"location": {
"path": {
"tag": "Field",
"model": "User",
"field": "id"
},
"directive": "id"
}
},
{
"tag": "CreateField",
"model": "User",
"field": "email",
"type": "String",
"arity": "Required"
},
{
"tag": "CreateDirective",
"location": {
"path": {
"tag": "Field",
"model": "User",
"field": "email"
},
"directive": "unique"
}
},
{
"tag": "CreateField",
"model": "User",
"field": "name",
"type": "String",
"arity": "Optional"
},
{
"tag": "CreateField",
"model": "User",
"field": "role",
"type": "String",
"arity": "Optional"
},
{
"tag": "CreateField",
"model": "User",
"field": "storeId",
"type": "Int",
"arity": "Optional"
},
{
"tag": "CreateModel",
"model": "Product"
},
{
"tag": "CreateField",
"model": "Product",
"field": "id",
"type": "Int",
"arity": "Required"
},
{
"tag": "CreateDirective",
"location": {
"path": {
"tag": "Field",
"model": "Product",
"field": "id"
},
"directive": "default"
}
},
{
"tag": "CreateArgument",
"location": {
"tag": "Directive",
"path": {
"tag": "Field",
"model": "Product",
"field": "id"
},
"directive": "default"
},
"argument": "",
"value": "autoincrement()"
},
{
"tag": "CreateDirective",
"location": {
"path": {
"tag": "Field",
"model": "Product",
"field": "id"
},
"directive": "id"
}
},
{
"tag": "CreateField",
"model": "Product",
"field": "handle",
"type": "String",
"arity": "Required"
},
{
"tag": "CreateDirective",
"location": {
"path": {
"tag": "Field",
"model": "Product",
"field": "handle"
},
"directive": "unique"
}
},
{
"tag": "CreateField",
"model": "Product",
"field": "name",
"type": "String",
"arity": "Optional"
},
{
"tag": "CreateField",
"model": "Product",
"field": "description",
"type": "String",
"arity": "Optional"
},
{
"tag": "CreateField",
"model": "Product",
"field": "price",
"type": "Int",
"arity": "Optional"
}
]
}

View File

@@ -0,0 +1,6 @@
# IF THERE'S A GIT CONFLICT IN THIS FILE, DON'T SOLVE IT MANUALLY!
# INSTEAD EXECUTE `prisma migrate fix`
# Prisma Migrate lockfile v1
# Read more about conflict resolution here: TODO
20200411130101-init

View File

@@ -0,0 +1,30 @@
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema
datasource db {
provider = "sqlite"
url = "file:./db.sqlite"
}
generator client {
provider = "prisma-client-js"
}
// --------------------------------------
model User {
id Int @default(autoincrement()) @id
email String @unique
name String?
role String?
storeId Int?
}
model Product {
id Int @default(autoincrement()) @id
handle String @unique
name String?
description String?
price Int?
}

View File

@@ -0,0 +1,3 @@
const {withBlitz} = require('@blitzjs/server')
module.exports = withBlitz({})

View File

@@ -0,0 +1,18 @@
{
"name": "store",
"version": "0.0.1-0.0.1-canary.4.0",
"private": true,
"scripts": {},
"dependencies": {
"@blitzjs/cli": "0.0.1-canary.1",
"@blitzjs/core": "0.0.1-0.0.1-canary.4.0",
"@blitzjs/server": "0.0.1-0.0.1-canary.4.0",
"@prisma/cli": "2.0.0-beta.2",
"@prisma/client": "2.0.0-beta.2",
"final-form": "4.19.1",
"react": "16.13.1",
"react-dom": "16.13.1",
"react-final-form": "6.4.0",
"typescript": "3.8.3"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -0,0 +1,10 @@
<svg width="82" height="16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill="url(#prefix__paint0_linear)" d="M9.018 0l9.019 16H0L9.018 0z"/>
<path fill="#333" fill-rule="evenodd" d="M51.634 12.028h-6.492V2.052h6.492v1.256H46.61v3.007h4.37V7.57h-4.37v3.202h5.024v1.255zm-14.063 0h-7.235v-1.096l5.342-7.624h-5.253V2.052h7.058v1.097l-5.342 7.623h5.43v1.256zm21.88 0h6.333v-1.256h-2.423V3.308h2.423V2.052h-6.332v1.256h2.441v7.465h-2.441v1.255zm18.22 0h-1.468v-8.72h-3.36V2.052h8.225v1.256H77.67v8.72z" clip-rule="evenodd"/>
<defs>
<linearGradient id="prefix__paint0_linear" x1="28.022" x2="16.189" y1="22.991" y2="8.569" gradientUnits="userSpaceOnUse">
<stop stop-color="#fff"/>
<stop offset="1"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 794 B

View File

@@ -0,0 +1,20 @@
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"baseUrl": "./",
"allowJs": true,
"skipLibCheck": true,
"strict": false,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve"
},
"exclude": ["node_modules"],
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"]
}

4
examples/vanilla-next/.gitignore vendored Normal file
View File

@@ -0,0 +1,4 @@
.next
.blitz
node_modules
dist

View File

@@ -0,0 +1 @@
This is an example of a simple next app being run using the blitz cli

View File

@@ -0,0 +1,3 @@
export function greet(who) {
return `Hello ${who}`
}

2
examples/vanilla-next/next-env.d.ts vendored Normal file
View File

@@ -0,0 +1,2 @@
/// <reference types="next" />
/// <reference types="next/types/global" />

View File

@@ -0,0 +1,3 @@
const {withBlitz} = require('@blitzjs/server')
module.exports = withBlitz({})

View File

@@ -0,0 +1,19 @@
{
"name": "vanilla-next",
"private": true,
"main": "index.js",
"author": "BlitzJS",
"license": "MIT",
"scripts": {
"start": "blitz start",
"build": "blitz build"
},
"dependencies": {
"@blitzjs/cli": "0.0.1-canary.0",
"@blitzjs/core": "0.0.1-canary.0",
"@blitzjs/server": "0.0.1-canary.0",
"next": "^9.3.0",
"react": "16.13.1",
"react-dom": "16.13.1"
}
}

View File

@@ -0,0 +1,13 @@
import React from 'react'
import Link from 'next/link'
export default function Page() {
return (
<h1>
I am home.{' '}
<Link href="/posts/5">
<a>Click here</a>
</Link>
</h1>
)
}

View File

@@ -0,0 +1,17 @@
import {greet} from 'app/posts/controller'
// TEST: Typical server rendering from params example
export const getServerSideProps = (ctx: any) => {
const {res} = ctx
const stringId = ctx.query && (Array.isArray(ctx.query.id) ? ctx.query.id[0] : ctx.query.id)
const id = isNaN(parseInt(stringId)) ? null : parseInt(stringId)
if (res?.status) res?.status(200)
return Promise.resolve({props: {id}})
}
export default function Page({id}) {
return <h1>{greet(`${id}`)}</h1>
}

View File

@@ -0,0 +1,34 @@
{
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
"exclude": ["node_modules", ".blitz"],
"compilerOptions": {
"target": "es5",
"module": "esnext",
"importHelpers": true,
"declaration": true,
"sourceMap": true,
"rootDir": "../../",
"baseUrl": "./",
"strict": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"strictPropertyInitialization": true,
"noImplicitThis": true,
"alwaysStrict": true,
"noUnusedLocals": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"moduleResolution": "node",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"downlevelIteration": true,
"noEmit": true,
"esModuleInterop": true,
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"noImplicitAny": false
}
}

16
lerna.json Normal file
View File

@@ -0,0 +1,16 @@
{
"version": "0.0.1-0.0.1-canary.4.0",
"packages": ["packages/*"],
"npmClient": "yarn",
"useWorkspaces": true,
"command": {
"version": {
"exact": true
},
"publish": {
"npmClient": "npm",
"allowBranch": ["master", "canary"],
"registry": "https://registry.npmjs.org/"
}
}
}

53
package.json Normal file
View File

@@ -0,0 +1,53 @@
{
"private": true,
"workspaces": {
"packages": [
"packages/*",
"examples/*"
],
"nohoist": [
"husky",
"**/@prisma",
"**/@prisma/**"
]
},
"engines": {
"yarn": "^1.19.1",
"node": ">=12.16.1"
},
"scripts": {
"dev": "lerna run start --scope @blitzjs/* --stream --parallel",
"build": "lerna run build --scope @blitzjs/* --stream",
"prepare": "lerna run build --scope @blitzjs/* && lerna run test --parallel",
"pretest": "lerna run build --scope @blitzjs/server --scope @blitzjs/core",
"test": "lerna run test --parallel"
},
"devDependencies": {
"@types/debug": "^4.1.5",
"@types/jest": "^25.1.3",
"@types/node": "^13.7.4",
"cross-env": "^7.0.0",
"debug": "^4.1.1",
"husky": "^4.2.3",
"jest": "24.9.0",
"lerna": "^3.20.2",
"lint-staged": "^10.0.8",
"npm-run-all": "^4.1.5",
"prettier": "^2.0.4",
"pretty-quick": "2.0.1",
"ts-jest": "24.3.0",
"tsdx": "^0.13.1",
"tslib": "^1.10.0",
"typescript": "^3.7.5"
},
"husky": {
"hooks": {
"pre-commit": "pretty-quick --staged",
"pre-push": "yarn test"
}
},
"resolutions": {
"jest": "24.9.0",
"ts-jest": "24.3.0"
}
}

13
packages/cli/.gitignore vendored Normal file
View File

@@ -0,0 +1,13 @@
*.log
.DS_Store
node_modules
.rts2_cache_cjs
.rts2_cache_esm
.rts2_cache_umd
.rts2_cache_system
dist
tmp
.blitz
# good directory to use for testing app generation
_app

160
packages/cli/README.md Normal file
View File

@@ -0,0 +1,160 @@
# blitz-cli
Blitz CLI
[![oclif](https://img.shields.io/badge/cli-oclif-brightgreen.svg)](https://oclif.io)
[![Version](https://img.shields.io/npm/v/blitz-cli.svg)](https://npmjs.org/package/blitz-cli)
[![Downloads/week](https://img.shields.io/npm/dw/blitz-cli.svg)](https://npmjs.org/package/blitz-cli)
[![License](https://img.shields.io/npm/l/blitz-cli.svg)](https://github.com/mabadir/blitz-cli/blob/master/package.json)
## Contributing
Run `yarn` from the monorepo root
**Run locally from this directory:**
`yarn b [COMMAND]`
**Run tests:**
`yarn test`
**Build package:**
`yarn build`
<!-- toc -->
* [blitz-cli](#blitz-cli)
* [Usage](#usage)
* [Commands](#commands)
<!-- tocstop -->
# Usage
<!-- usage -->
```sh-session
$ npm install -g @blitzjs/cli
$ blitz COMMAND
running command...
$ blitz (-v|--version|version)
@blitzjs/cli/0.0.1-canary.1 darwin-x64 node-v12.16.1
$ blitz --help [COMMAND]
USAGE
$ blitz COMMAND
...
```
<!-- usagestop -->
# Commands
<!-- commands -->
* [`blitz build`](#blitz-build)
* [`blitz console`](#blitz-console)
* [`blitz db COMMAND`](#blitz-db-command)
* [`blitz help [COMMAND]`](#blitz-help-command)
* [`blitz new [PATH]`](#blitz-new-path)
* [`blitz start`](#blitz-start)
* [`blitz test [WATCH]`](#blitz-test-watch)
## `blitz build`
Create a production build
```
USAGE
$ blitz build
ALIASES
$ blitz b
```
## `blitz console`
Run project REPL
```
USAGE
$ blitz console
ALIASES
$ blitz c
```
## `blitz db COMMAND`
Run project database commands
```
USAGE
$ blitz db COMMAND
ARGUMENTS
COMMAND Run specific db command
```
_See code: [lib/commands/db.js](https://github.com/blitz-js/blitz/blob/v0.0.1-canary.1/lib/commands/db.js)_
## `blitz help [COMMAND]`
display help for blitz
```
USAGE
$ blitz help [COMMAND]
ARGUMENTS
COMMAND command to show help for
OPTIONS
--all see all commands in CLI
```
_See code: [@oclif/plugin-help](https://github.com/oclif/plugin-help/blob/v2.2.3/src/commands/help.ts)_
## `blitz new [PATH]`
Create a new Blitz project
```
USAGE
$ blitz new [PATH]
ARGUMENTS
PATH path to the new project, defaults to the current directory
OPTIONS
-h, --help show CLI help
-t, --[no-]ts generate a TypeScript project
--dry-run show what files will be created without writing them to disk
--[no-]yarn use Yarn as the package manager
```
## `blitz start`
Start a development server
```
USAGE
$ blitz start
OPTIONS
-p, --production Create and start a production server
ALIASES
$ blitz s
```
## `blitz test [WATCH]`
Run project tests
```
USAGE
$ blitz test [WATCH]
ARGUMENTS
WATCH Run test:watch
ALIASES
$ blitz t
```
_See code: [lib/commands/test.js](https://github.com/blitz-js/blitz/blob/v0.0.1-canary.1/lib/commands/test.js)_
<!-- commandsstop -->

3
packages/cli/bin/run Executable file
View File

@@ -0,0 +1,3 @@
#!/usr/bin/env node
require('@oclif/command').run().then(require('@oclif/command/flush')).catch(require('@oclif/errors/handle'))

3
packages/cli/bin/run.cmd Normal file
View File

@@ -0,0 +1,3 @@
@echo off
node "%~dp0\run" %*

View File

@@ -0,0 +1,26 @@
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
moduleFileExtensions: ['ts', 'js', 'json'],
coverageReporters: ['json', 'lcov', 'text', 'clover'],
// collectCoverage: !!`Boolean(process.env.CI)`,
collectCoverageFrom: ['src/**/*.ts'],
coveragePathIgnorePatterns: ['/templates/'],
modulePathIgnorePatterns: ['tmp', 'lib'],
testPathIgnorePatterns: ['src/commands/test.ts'],
// TODO enable threshold
// coverageThreshold: {
// global: {
// branches: 100,
// functions: 100,
// lines: 100,
// statements: 100,
// },
// },
globals: {
'ts-jest': {
tsConfig: 'test/tsconfig.json',
},
},
}

76
packages/cli/package.json Normal file
View File

@@ -0,0 +1,76 @@
{
"name": "@blitzjs/cli",
"description": "Blitz CLI",
"version": "0.0.1-0.0.1-canary.4.0",
"license": "MIT",
"scripts": {
"b": "./bin/run",
"build": "rimraf lib && tsc && cp -r templates lib/templates",
"test": "jest --coverage",
"test:watch": "jest --watch"
},
"author": {
"name": "Brandon Bayer",
"email": "b@bayer.ws",
"url": "https://twitter.com/flybayer"
},
"main": "lib/index.js",
"bin": {
"blitz": "./bin/run"
},
"files": [
"/bin",
"/lib"
],
"dependencies": {
"@blitzjs/server": "0.0.1-0.0.1-canary.4.0",
"@oclif/command": "^1.5.19",
"@oclif/config": "^1.14.0",
"@oclif/plugin-help": "^2.2.3",
"@oclif/plugin-not-found": "^1.2.3",
"chalk": "^4.0.0",
"chokidar": "^3.3.1",
"diff": "^4.0.2",
"enquirer": "^2.3.4",
"execa": "^4.0.0",
"fs-extra": "^9.0.0",
"has-yarn": "^2.1.0",
"mem-fs": "^1.1.3",
"mem-fs-editor": "^6.0.0",
"vinyl": "^2.2.0"
},
"devDependencies": {
"@oclif/dev-cli": "^1.22.2",
"@oclif/test": "^1.2.5",
"@types/diff": "^4.0.2",
"@types/fs-extra": "^8.1.0",
"@types/mem-fs": "^1.1.2",
"@types/mem-fs-editor": "^5.1.1",
"@types/vinyl": "^2.0.4",
"chai": "^4.2.0",
"globby": "^11.0.0",
"rimraf": "^3.0.2",
"ts-node": "^8.6.2"
},
"oclif": {
"commands": "./lib/src/commands",
"bin": "blitz",
"plugins": [
"@oclif/plugin-help",
"@oclif/plugin-not-found"
]
},
"engines": {
"yarn": "^1.19.1",
"node": ">=12.16.1"
},
"keywords": [
"blitz",
"cli"
],
"repository": {
"type": "git",
"url": "https://github.com/blitz-js/blitz"
},
"gitHead": "6719104cb3e78948e7f06aa948ff72bbb84cb682"
}

View File

@@ -0,0 +1,8 @@
import {Command as OclifCommand} from '@oclif/command'
import Enquirer = require('enquirer')
abstract class Command extends OclifCommand {
protected enquirer = new Enquirer()
}
export default Command

View File

@@ -0,0 +1,15 @@
import {Command} from '@oclif/command'
import {build} from '@blitzjs/server'
export default class Build extends Command {
static description = 'Create a production build'
static aliases = ['b']
async run() {
const config = {
rootFolder: process.cwd(),
}
await build(config)
}
}

View File

@@ -0,0 +1,70 @@
import {Command} from '@oclif/command'
import * as REPL from 'repl'
import {REPLCommand, REPLServer} from 'repl'
import {watch} from 'chokidar'
import {loadDependencies} from '../utils/load-dependencies'
import {BLITZ_MODULE_PATHS, loadBlitz} from '../utils/load-blitz'
export default class Console extends Command {
static description = 'Run project REPL'
static aliases = ['c']
static message = `Welcome to Blitz.js v0.0.1
Type ".help" for more information.`
static replOptions = {
prompt: '⚡️>',
useColors: true,
}
static commands = {
reload: {
help: 'Reload all modules',
action(this: REPLServer) {
this.clearBufferedCommand()
console.log('Reloading all modules...')
Console.loadModules(this)
this.displayPrompt()
},
},
}
async run() {
this.log(Console.message)
const repl = Console.initializeRepl()
const watchers = [
watch('package.json').on('change', () => Console.loadDependencies(repl)),
watch(BLITZ_MODULE_PATHS).on('all', () => Console.loadBlitz(repl)),
]
repl
.on('reset', () => Console.loadModules(repl))
.on('exit', () => watchers.forEach((watcher) => watcher.close()))
}
private static initializeRepl() {
const repl = REPL.start(Console.replOptions)
Console.defineCommands(repl, Console.commands)
Console.loadModules(repl)
return repl
}
private static loadModules(repl: REPLServer) {
// Console.loadDependencies(repl)
Console.loadBlitz(repl)
}
private static loadDependencies(repl: REPLServer) {
Object.assign(repl.context, loadDependencies(process.cwd()))
}
private static loadBlitz(repl: REPLServer) {
Object.assign(repl.context, loadBlitz())
}
private static defineCommands(repl: REPLServer, commands: Record<string, REPLCommand>) {
Object.entries(commands).forEach(([keyword, cmd]) => repl.defineCommand(keyword, cmd))
}
}

View File

@@ -0,0 +1,48 @@
import {platform} from 'os'
import {spawn} from 'child_process'
import {Command} from '@oclif/command'
import * as path from 'path'
export default class Db extends Command {
static description = 'Run project database commands'
static args = [
{
name: 'command',
description: 'Run specific db command',
required: true,
},
]
async run() {
const {args} = this.parse(Db)
const command = args['command']
const prismaBinaryName = platform() === 'win32' ? 'prisma.cmd' : 'prisma'
const prismaBinary = path.join(process.cwd(), 'node_modules/.bin', prismaBinaryName)
const schemaArg = `--schema=${path.join(process.cwd(), 'db', 'schema.prisma')}`
if (command === 'migrate' || command === 'm') {
const cp = spawn(prismaBinary, ['migrate', 'save', schemaArg, '--create-db', '--experimental'], {
stdio: 'inherit',
})
cp.on('exit', (code: number) => {
if (code == 0) {
const cp = spawn(prismaBinary, ['migrate', 'up', schemaArg, '--create-db', '--experimental'], {
stdio: 'inherit',
})
cp.on('exit', (code: number) => {
if (code == 0) {
spawn(prismaBinary, ['generate', schemaArg], {stdio: 'inherit'})
}
})
}
})
} else if (command === 'init' || command === 'i') {
spawn(prismaBinary, ['init'], {stdio: 'inherit'})
} else {
this.log('Missing command')
}
}
}

View File

@@ -0,0 +1,63 @@
import * as path from 'path'
import {flags} from '@oclif/command'
import Command from '../command'
import AppGenerator from '../generators/app'
const debug = require('debug')('blitz:new')
import PromptAbortedError from '../errors/prompt-aborted'
export interface Flags {
ts: boolean
yarn: boolean
}
export default class New extends Command {
static description = 'Create a new Blitz project'
static args = [
{
name: 'path',
required: false,
description: 'path to the new project, defaults to the current directory',
},
]
static flags = {
help: flags.help({char: 'h'}),
ts: flags.boolean({
char: 't',
description: 'generate a TypeScript project',
default: true,
allowNo: true,
}),
yarn: flags.boolean({description: 'use Yarn as the package manager', default: true, allowNo: true}),
'dry-run': flags.boolean({description: 'show what files will be created without writing them to disk'}),
}
async run() {
const {args, flags} = this.parse(New)
debug('args: ', args)
debug('flags: ', flags)
const destinationRoot = args?.path ? path.resolve(args?.path) : process.cwd()
const appName = path.basename(destinationRoot)
const generator = new AppGenerator({
sourceRoot: path.join(__dirname, '../../templates/app'),
destinationRoot,
appName,
dryRun: flags['dry-run'],
install: true,
yarn: flags.yarn,
})
try {
await generator.run()
this.log('App Created!')
} catch (err) {
if (err instanceof PromptAbortedError) this.exit(0)
this.error(err)
}
}
}

View File

@@ -0,0 +1,28 @@
import {Command, flags} from '@oclif/command'
import {dev, prod} from '@blitzjs/server'
export default class Start extends Command {
static description = 'Start a development server'
static aliases = ['s']
static flags = {
production: flags.boolean({
char: 'p',
description: 'Create and start a production server',
}),
}
async run() {
const {flags} = this.parse(Start)
const config = {
rootFolder: process.cwd(),
}
if (flags.production) {
await prod(config)
} else {
await dev(config)
}
}
}

View File

@@ -0,0 +1,31 @@
import {platform} from 'os'
import {spawn} from 'child_process'
import {Command} from '@oclif/command'
import hasYarn from 'has-yarn'
export default class Test extends Command {
static description = 'Run project tests'
static aliases = ['t']
static args = [
{
name: 'watch',
description: 'Run test:watch',
},
]
async run() {
const {args} = this.parse(Test)
let watchMode: boolean = false
const watch = args['watch']
if (watch) {
watchMode = watch === 'watch' || watch === 'w'
}
const yarnBinary = platform() === 'win32' ? 'yarn.cmd' : 'yarn'
const npmBinary = platform() === 'win32' ? 'npm.cmd' : 'npm'
const packageManager = hasYarn() ? yarnBinary : npmBinary
if (watchMode) spawn(packageManager, ['test:watch'], {stdio: 'inherit'})
else spawn(packageManager, ['test'], {stdio: 'inherit'})
}
}

View File

@@ -0,0 +1,5 @@
export default class PromptAbortedError extends Error {
constructor() {
super('Prompt aborted')
}
}

View File

@@ -0,0 +1,87 @@
import * as fs from 'fs-extra'
import * as path from 'path'
import {EventEmitter} from 'events'
import {create as createStore, Store} from 'mem-fs'
import {create as createEditor, Editor} from 'mem-fs-editor'
import Enquirer = require('enquirer')
import execa = require('execa')
import ConflictChecker from './transforms/conflict-checker'
export interface GeneratorOptions {
sourceRoot: string
destinationRoot?: string
yarn?: boolean
install?: boolean
dryRun?: boolean
}
/**
* The base generator class.
* Every generator must extend this class.
*/
abstract class Generator<T extends GeneratorOptions = GeneratorOptions> extends EventEmitter {
private readonly store: Store
protected readonly fs: Editor
protected readonly enquirer: Enquirer
private performedActions: string[] = []
constructor(protected readonly options: T) {
super()
this.store = createStore()
this.fs = createEditor(this.store)
this.enquirer = new Enquirer()
if (!this.options.destinationRoot) this.options.destinationRoot = process.cwd()
}
abstract async write(): Promise<void>
sourcePath(...paths: string[]): string {
return path.join(this.options.sourceRoot, ...paths)
}
destinationPath(...paths: string[]): string {
return path.join(this.options.destinationRoot!, ...paths)
}
async install() {
await execa(this.options.yarn ? 'yarn' : 'npm', ['install'])
}
async run() {
if (!this.options.dryRun) {
await fs.ensureDir(this.options.destinationRoot!)
process.chdir(this.options.destinationRoot!)
}
await this.write()
await new Promise((resolve, reject) => {
const conflictChecker = new ConflictChecker({
dryRun: this.options.dryRun,
})
conflictChecker.on('error', (err) => {
reject(err)
})
conflictChecker.on('fileStatus', (data: string) => {
this.performedActions.push(data)
})
this.fs.commit([conflictChecker], (err) => {
if (err) reject(err)
resolve()
})
})
this.performedActions.forEach((action) => {
console.log(action)
})
if (this.options.install && !this.options.dryRun) await this.install()
}
}
export default Generator

View File

@@ -0,0 +1,35 @@
import Generator, {GeneratorOptions} from '../generator'
export interface AppGeneratorOptions extends GeneratorOptions {
appName: string
}
class AppGenerator extends Generator<AppGeneratorOptions> {
packageJson() {
return {
name: this.options.appName,
version: '0.0.1',
private: true,
scripts: {
dev: 'next dev',
build: 'next build',
start: 'next start',
},
}
}
async write() {
this.fs.copyTpl(this.sourcePath('README.md.ejs'), this.destinationPath('README.md'), {
name: 'Hello',
})
this.fs.copyTpl(this.sourcePath('pages/index.js.ejs'), this.destinationPath('pages/index.js'), {
name: 'Hello',
})
this.fs.writeJSON(this.destinationPath('package.json'), this.packageJson())
this.fs.copy(this.sourcePath('gitignore'), this.destinationPath('.gitignore'))
}
}
export default AppGenerator

View File

@@ -0,0 +1 @@
export {run} from '@oclif/command'

View File

@@ -0,0 +1,142 @@
import {Transform, TransformCallback} from 'stream'
import * as path from 'path'
import File from 'vinyl'
import {diffLines, Change} from 'diff'
import * as fs from 'fs-extra'
import chalk = require('chalk')
import enquirer = require('enquirer')
import PromptAbortedError from '../errors/prompt-aborted'
interface PromptAnswer {
action: 'overwrite' | 'skip' | 'show'
}
type PromptActions = 'create' | 'overwrite' | 'skip' | 'identical'
interface ConflictCheckerOptions {
dryRun?: boolean
}
export default class ConflictChecker extends Transform {
private _destroyed = false
constructor(private readonly options?: ConflictCheckerOptions) {
super({
objectMode: true,
})
}
_transform(file: File, _encoding: string, cb: TransformCallback): void {
if (file.state === null) {
cb()
return
}
// If the file doesn't exists yet there isn't any diff to perform
const filePath = path.resolve(file.path)
if (!fs.existsSync(filePath)) {
this.handlePush(file, 'create')
cb()
return
}
this.checkDiff(file)
.then((status) => {
if (status !== 'skip') {
this.handlePush(file, status)
} else {
this.fileStatusString(file, status)
}
cb()
})
.catch((err) => {
// If the error is an empty string, it means that the user has
// stopped the prompt with ctrl-c so we return PromptAbortedError
// to end the program without writing anything to disk
cb(err || new PromptAbortedError())
})
}
destroy(err?: Error): void {
if (this._destroyed) return
this._destroyed = true
process.nextTick(() => {
if (err) this.emit('err', err)
this.emit('close')
})
}
handlePush(file: File, status: PromptActions): void {
if (!this.options?.dryRun) this.push(file)
this.emit('fileStatus', this.fileStatusString(file, status))
}
private async checkDiff(file: File): Promise<PromptActions> {
let newFileContents = file.contents?.toString() ?? ''
const oldFileContents = fs.readFileSync(path.resolve(file.path)).toString()
const diff = diffLines(oldFileContents, newFileContents)
const conflict = diff.some((line) => line.added || line.removed)
if (conflict) {
let answer = null
do {
answer = await enquirer.prompt<PromptAnswer>({
type: 'select',
name: 'action',
message: `The file "${file.path}" has conflicts. What do you want to do?`, // Maybe color file.path
choices: [
{name: 'overwrite', message: 'Overwrite', value: 'overwrite'},
{name: 'skip', message: 'Skip', value: 'skip'},
{name: 'show', message: 'Show changes', value: 'show'},
],
})
if (answer?.action === 'show') this.printDiff(diff)
} while (answer?.action === 'show')
return answer.action
}
return 'identical'
}
private printDiff(diff: Change[]) {
console.log('\n')
diff.forEach((line) => {
const value = line.value.replace('\n', '')
if (line.added) {
console.log(chalk.green(`+ ${value}`))
} else if (line.removed) {
console.log(chalk.red(`- ${value}`))
} else {
console.log(value)
}
})
console.log('\n')
}
private fileStatusString(file: File, status: PromptActions) {
let statusLog = null
switch (status) {
case 'create':
statusLog = chalk.green('CREATE ')
break
case 'overwrite':
statusLog = chalk.cyan('OVERWRITE')
break
case 'skip':
statusLog = chalk.blue('SKIP ')
break
case 'identical':
statusLog = chalk.gray('IDENTICAL')
}
return `${statusLog} ${file.relative}`
}
}

View File

@@ -0,0 +1,7 @@
import {forceRequire} from './module'
export const BLITZ_MODULE_PATHS = ['@prisma/client']
export const loadBlitz = () => {
return Object.assign({}, ...BLITZ_MODULE_PATHS.map(forceRequire))
}

View File

@@ -0,0 +1,42 @@
import * as path from 'path'
import {forceRequire} from './module'
const modulePath = (module: string) => {
try {
return require.resolve(module)
} catch (e) {
throw new Error(`Failed to load module '${module}'`)
}
}
const isFunction = (functionToCheck: any): functionToCheck is Function =>
typeof functionToCheck === 'function'
const toCamelCase = (name: string) =>
name
.replace(/[_-]([a-z])/g, (_match: string, group: string) => group.toUpperCase())
.replace(/@[a-z]+\//, '')
const functionModuleName = (moduleName: string, fun: Function) => {
if (fun.name && fun.name !== 'anonymous') return fun.name
return toCamelCase(moduleName)
}
export const loadDependencies = (pkgRoot: string) => {
const pkg = forceRequire(path.join(pkgRoot, 'package.json'))
const modules = Object.keys(pkg.dependencies || {})
.map((name) => [name, modulePath(name)])
.filter(([_name, path]) => path !== null)
.map(([name, path]) => [name, forceRequire(path)])
.map(([name, module]) => {
if (isFunction(module)) return {[functionModuleName(name, module)]: module}
const defaultExport = module.default
if (!defaultExport) return module
if (isFunction(defaultExport)) return {[functionModuleName(name, defaultExport)]: defaultExport}
return {
[toCamelCase(defaultExport)]: defaultExport,
}
})
return Object.assign({}, ...modules)
}

View File

@@ -0,0 +1,8 @@
const invalidateCache = (module: string) => {
delete require.cache[require.resolve(module)]
}
export const forceRequire = (module: string) => {
invalidateCache(module)
return require(module)
}

View File

@@ -0,0 +1 @@
# <%= name %>

View File

@@ -0,0 +1,25 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
# misc
.DS_Store
.env*
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*

View File

@@ -0,0 +1,15 @@
import Head from 'next/head'
const Home = () => (
<div className="container">
<Head>
<title><%= name %></title>
</Head>
<main>
<h1>Welcome to <%= name %>!</h1>
</main>
</div>
)
export default Home

View File

@@ -0,0 +1,20 @@
const build = jest.fn(() => {})
jest.mock('@blitzjs/server', () => ({build}))
import BuildCmd from '../../src/commands/build'
import {resolve} from 'path'
describe('Build command', () => {
beforeEach(() => {
jest.clearAllMocks()
})
const options = {
rootFolder: resolve(__dirname, '../../'),
}
it('runs the build script', async () => {
await BuildCmd.run([])
expect(build).toBeCalledWith(options)
})
})

View File

@@ -0,0 +1,49 @@
import * as repl from 'repl'
import * as chokidar from 'chokidar'
import ConsoleCmd from '../../src/commands/console'
import {BLITZ_MODULE_PATHS} from '../../src/utils/load-blitz'
import {REPLServer} from 'repl'
import {FSWatcher} from 'chokidar'
const mockRepl = ({
defineCommand: jest.fn(),
on: jest.fn(),
context: {},
} as any) as REPLServer
const mockWatcher = ({
on: jest.fn(),
} as any) as FSWatcher
jest.mock('repl')
jest.mock('chokidar')
jest.mock(`${process.cwd()}/package.json`, () => ({
dependencies: {
ramda: '1.0.0',
},
}))
jest.mock('../../src/utils/load-dependencies')
jest.mock('../../src/utils/load-blitz')
describe('Console command', () => {
beforeEach(() => {
jest.resetAllMocks()
})
it('runs REPL', async () => {
jest.spyOn(ConsoleCmd.prototype, 'log')
jest.spyOn(repl, 'start').mockReturnValue(mockRepl)
jest.spyOn(chokidar, 'watch').mockReturnValue(mockWatcher)
jest.spyOn(mockRepl, 'on').mockReturnValue(mockRepl)
await ConsoleCmd.prototype.run()
expect(repl.start).toBeCalledWith(ConsoleCmd.replOptions)
expect(mockRepl.defineCommand).toBeCalledWith('reload', ConsoleCmd.commands.reload)
expect(chokidar.watch).toBeCalledWith('package.json')
expect(chokidar.watch).toBeCalledWith(BLITZ_MODULE_PATHS)
expect(ConsoleCmd.prototype.log).toHaveBeenCalledWith(`Welcome to Blitz.js v0.0.1
Type ".help" for more information.`)
})
})

View File

@@ -0,0 +1,78 @@
import * as path from 'path'
import {platform} from 'os'
let onSpy: jest.Mock
const spawn = jest.fn(() => {
onSpy = jest.fn(function on(_: string, callback: (_: number) => {}) {
callback(0)
})
return {on: onSpy}
})
jest.doMock('child_process', () => ({spawn}))
import DbCmd from '../../src/commands/db'
const prismaBinaryName = platform() === 'win32' ? 'prisma.cmd' : 'prisma'
const prismaBinary = path.join(process.cwd(), 'node_modules/.bin', prismaBinaryName)
const schemaArg = `--schema=${path.join(process.cwd(), 'db', 'schema.prisma')}`
const initParams = [prismaBinary, ['init'], {stdio: 'inherit'}]
const migrateSaveParams = [
prismaBinary,
['migrate', 'save', schemaArg, '--create-db', '--experimental'],
{stdio: 'inherit'},
]
const migrateUpParams = [
prismaBinary,
['migrate', 'up', schemaArg, '--create-db', '--experimental'],
{stdio: 'inherit'},
]
describe('Db command', () => {
beforeEach(() => {
jest.clearAllMocks()
})
it('runs db init', async () => {
await DbCmd.run(['init'])
expect(spawn).toHaveBeenCalledWith(...initParams)
})
it('runs db init (alias)', async () => {
await DbCmd.run(['i'])
expect(spawn).toHaveBeenCalledWith(...initParams)
})
it('runs db migrate', async () => {
await DbCmd.run(['migrate'])
expect(spawn).toBeCalledWith(...migrateSaveParams)
expect(spawn.mock.calls.length).toBe(3)
// following expection is not working
//expect(onSpy).toHaveBeenCalledWith(0);
expect(spawn).toBeCalledWith(...migrateUpParams)
})
it('runs db migrate (alias)', async () => {
await DbCmd.run(['m'])
expect(spawn).toBeCalledWith(...migrateSaveParams)
expect(spawn.mock.calls.length).toBe(3)
// following expection is not working
//expect(onSpy).toHaveBeenCalledWith(0);
expect(spawn).toBeCalledWith(...migrateUpParams)
})
it('does not run db in case of invalid command', async () => {
await DbCmd.run(['invalid'])
expect(spawn.mock.calls.length).toBe(0)
})
})

View File

@@ -0,0 +1,3 @@
jest.mock('fs')
test('Mock inquirer.js and test the command', () => {})

View File

@@ -0,0 +1,27 @@
const dev = jest.fn(() => {})
const prod = jest.fn(() => {})
jest.mock('@blitzjs/server', () => ({dev, prod}))
import StartCmd from '../../src/commands/start'
import {resolve} from 'path'
describe('Start command', () => {
beforeEach(() => {
jest.clearAllMocks()
})
const options = {
rootFolder: resolve(__dirname, '../../'),
}
it('runs the dev script', async () => {
await StartCmd.run([])
expect(dev).toBeCalledWith(options)
})
it('runs the prod script when passed the production flag', async () => {
await StartCmd.run(['--production'])
expect(prod).toBeCalledWith(options)
})
})

View File

@@ -0,0 +1,55 @@
import childProcess from 'child_process'
import hasYarn from 'has-yarn'
import TestCmd from '../../src/commands/test'
jest.mock('child_process')
jest.mock('has-yarn')
const testParams = [['test'], {stdio: 'inherit'}]
const testWatchParams = [['test:watch'], {stdio: 'inherit'}]
describe('Test command', () => {
beforeEach(() => {
jest.clearAllMocks()
})
it('runs yarn test script', async () => {
jest.spyOn(hasYarn, 'default').mockReturnValue(true)
await TestCmd.run([])
expect(childProcess.spawn).toBeCalledWith('yarn', ...testParams)
})
it('runs npm test script', async () => {
jest.spyOn(hasYarn, 'default').mockReturnValue(false)
await TestCmd.run([])
expect(childProcess.spawn).toBeCalledWith('npm', ...testParams)
})
it('runs yarn test:watch script', async () => {
jest.spyOn(hasYarn, 'default').mockReturnValue(true)
await TestCmd.run(['watch'])
expect(childProcess.spawn).toBeCalledWith('yarn', ...testWatchParams)
})
it('runs yarn test:watch script with alias', async () => {
jest.spyOn(hasYarn, 'default').mockReturnValue(true)
await TestCmd.run(['w'])
expect(childProcess.spawn).toBeCalledWith('yarn', ...testWatchParams)
})
it('runs yarn test and ignores invalid argument', async () => {
jest.spyOn(hasYarn, 'default').mockReturnValue(true)
await TestCmd.run(['invalid'])
expect(childProcess.spawn).toBeCalledWith('yarn', ...testParams)
})
})

View File

@@ -0,0 +1,9 @@
{
"extends": "../tsconfig",
"compilerOptions": {
"noEmit": true,
"types": ["jest"],
"target": "es6"
},
"references": [{"path": ".."}]
}

View File

@@ -0,0 +1,15 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"target": "es2015",
"baseUrl": "./",
"module": "commonjs",
"rootDir": ".",
"outDir": "lib",
"declaration": false,
"sourceMap": false,
"types": []
},
"include": ["src/**/*"],
"exclude": ["node_modules"]
}

2721
packages/cli/yarn.lock Normal file

File diff suppressed because it is too large Load Diff

8
packages/core/.gitignore vendored Normal file
View File

@@ -0,0 +1,8 @@
*.log
.DS_Store
node_modules
.rts2_cache_cjs
.rts2_cache_esm
.rts2_cache_umd
.rts2_cache_system
dist

View File

@@ -0,0 +1 @@
require('@testing-library/jest-dom')

View File

@@ -0,0 +1,70 @@
{
"name": "@blitzjs/core",
"description": "Framework for building monolithic, full-stack, serverless React apps with zero data-fetching and zero client-side state management",
"version": "0.0.1-0.0.1-canary.4.0",
"license": "MIT",
"scripts": {
"start": "tsdx watch",
"build": "tsdx build",
"test": "tsdx test",
"test:watch": "tsdx test --watch",
"lint": "tsdx lint"
},
"author": {
"name": "Brandon Bayer",
"email": "b@bayer.ws",
"url": "https://twitter.com/flybayer"
},
"contributors": [
{
"name": "Michael Edelman",
"email": "michael@fabulas.io",
"url": "https://twitter.com/edelman215"
}
],
"main": "dist/index.js",
"module": "dist/core.esm.js",
"types": "dist/packages/core/src/index.d.ts",
"files": [
"dist"
],
"husky": {
"hooks": {
"pre-commit": "tsdx lint"
}
},
"jest": {
"setupFilesAfterEnv": [
"<rootDir>/jest.setup.js"
]
},
"engines": {
"yarn": "^1.19.1",
"node": ">=12.16.1"
},
"repository": {
"type": "git",
"url": "https://github.com/blitz-js/blitz"
},
"devDependencies": {
"@prisma/client": "2.0.0-beta.2",
"@testing-library/jest-dom": "5.5.0",
"@testing-library/react": "10.0.2",
"@types/jest": "^25.1.3",
"@types/node": "^13.7.4",
"@types/react": "16.9.34",
"cross-env": "^7.0.0",
"husky": "^4.2.3",
"lint-staged": "^10.0.8",
"next": "^9.3.0",
"ts-jest": "24.3.0"
},
"dependencies": {
"@types/react-query": "1.1.2",
"@types/serialize-error": "4.0.1",
"pretty-ms": "6.0.1",
"react-query": "1.2.1",
"serialize-error": "6.0.0"
},
"gitHead": "6719104cb3e78948e7f06aa948ff72bbb84cb682"
}

View File

@@ -0,0 +1,14 @@
export {
GetStaticProps,
GetStaticPaths,
GetServerSideProps,
NextPage as BlitzPage,
NextApiRequest as BlitzApiRequest,
NextApiResponse as BlitzApiResponse,
} from 'next'
export {default as Link} from 'next/link'
export {default as Router, useRouter, withRouter} from 'next/router'
export * from './useQuery'
export * from './rpc'

95
packages/core/src/rpc.ts Normal file
View File

@@ -0,0 +1,95 @@
import {BlitzApiRequest, BlitzApiResponse} from '.'
import {serializeError, deserializeError} from 'serialize-error'
export async function rpc(url: string, params: any) {
if (typeof window === 'undefined') return
const result = await window.fetch(url, {
method: 'POST',
credentials: 'same-origin',
headers: {
'Content-Type': 'application/json',
},
redirect: 'follow',
body: JSON.stringify({params}),
})
const json = await result.json()
if (json.result) {
return json.result
} else {
throw deserializeError(json.error)
}
}
rpc.warm = (url: string) => {
if (typeof window !== 'undefined') {
window.fetch(url, {method: 'HEAD'})
}
}
export function isomorphicRpc(resolver: any, cacheKey: string) {
if (typeof window !== 'undefined') {
const url = cacheKey.replace(/^app\/_rpc/, '/api')
const rpcFn: any = (params: any) => rpc(url, params)
rpcFn.cacheKey = url
// Warm the lambda
rpc.warm(url)
return rpcFn
} else {
return resolver
}
}
export function rpcHandler(
type: string,
name: string,
resolver: (...args: any) => Promise<any>,
connectDb?: () => any,
) {
return async function (req: BlitzApiRequest, res: BlitzApiResponse) {
const logPrefix = `[${type}:${name}]`
console.log(`${logPrefix} ${req.method} ${JSON.stringify(req.body)} `)
if (req.method === 'HEAD') {
// Warm the lamda and connect to DB
if (typeof connectDb === 'function') {
connectDb()
}
console.log(`${logPrefix} SUCCESS 200`)
return res.status(200).end()
} else if (req.method === 'POST') {
// Handle RPC call
if (typeof req.body.params === 'undefined') {
const error = {message: "Request body is missing the 'params' key"}
console.log(`${logPrefix} ERROR: ${JSON.stringify(error)}`)
return res.status(400).json({
result: null,
error,
})
}
try {
const result = await resolver(req.body.params)
console.log(`${logPrefix} SUCCESS ${JSON.stringify(result)}`)
return res.json({
result,
error: null,
})
} catch (error) {
console.log(`${logPrefix} ERROR: ${error}`)
return res.json({
result: null,
error: serializeError(error),
})
}
} else {
// Everything else is error
console.log(`${logPrefix} ERROR: 404`)
return res.status(404).end()
}
}
}

View File

@@ -0,0 +1,24 @@
import {useQuery as useReactQuery} from 'react-query'
import {PromiseReturnType} from '@prisma/client'
// TODO: why doesn't the type work properly from ../types?
// import {PromiseReturnType} from '../types'
type QueryFn = (...args: any) => Promise<any>
// interface QueryFn {
// (...args: any): Promise<any>
// cacheKey: string
// }
export function useQuery<T extends QueryFn>(
queryFn: T,
params?: any,
options: any = {},
): [PromiseReturnType<T>, Record<any, any>] {
const {data, ...rest} = useReactQuery([(queryFn as any).cacheKey, params], (_, params) => queryFn(params), {
...options,
suspense: false,
})
return [data as PromiseReturnType<T>, rest]
}

View File

@@ -0,0 +1 @@
export const isServer = typeof window === 'undefined'

View File

@@ -0,0 +1,3 @@
it('todo', async () => {
expect(true).toBe(true)
})

View File

@@ -0,0 +1,13 @@
{
"extends": "../../tsconfig.json",
"include": ["src", "types", "test"],
"exclude": ["node_modules"],
"compilerOptions": {
"baseUrl": "./",
"declarationDir": "./dist",
"downlevelIteration": true,
"paths": {
"*": ["src/*", "node_modules/*"]
}
}
}

View File

@@ -0,0 +1,6 @@
module.exports = {
rollup(config, options) {
config.external('@prisma/client')
return config
},
}

15
packages/core/types/index.d.ts vendored Normal file
View File

@@ -0,0 +1,15 @@
// declare module '@prisma/client' {
// export class PrismaClient {
// constructor(args: any)
// }
// }
/**
* Get the type of the value, that the Promise holds.
*/
export type PromiseType<T extends PromiseLike<any>> = T extends PromiseLike<infer U> ? U : T
/**
* Get the return type of a function which returns a Promise.
*/
export type PromiseReturnType<T extends (...args: any) => Promise<any>> = PromiseType<ReturnType<T>>

4
packages/server/.gitignore vendored Normal file
View File

@@ -0,0 +1,4 @@
*.log
.DS_Store
node_modules
dist

View File

@@ -0,0 +1,5 @@
#!/usr/bin/env node
'use strict'
require('@blitzjs/server/register')
require('next/dist/bin/next')

View File

@@ -0,0 +1,61 @@
{
"version": "0.0.1-0.0.1-canary.4.0",
"license": "MIT",
"main": "dist/index.js",
"bin": {
"next-patched": "./bin/next-patched"
},
"files": [
"dist"
],
"scripts": {
"start": "tsdx watch",
"build": "tsdx build",
"test": "tsdx test",
"lint": "tsdx lint"
},
"husky": {
"hooks": {
"pre-commit": "tsdx lint"
}
},
"name": "@blitzjs/server",
"author": {
"name": "Brandon Bayer",
"email": "b@bayer.ws",
"url": "https://twitter.com/flybayer"
},
"module": "dist/webpack.esm.js",
"types": "dist/packages/server/src/index.d.ts",
"devDependencies": {
"@types/gulp-if": "^0.0.33",
"@types/jest": "^25.1.3",
"@types/path-is-absolute": "^1.0.0",
"@types/readable-stream": "^2.3.5",
"@types/through2": "^2.0.34",
"@types/vinyl-fs": "^2.4.11",
"directory-tree": "^2.2.4",
"husky": "^4.2.3",
"tsdx": "^0.13.1",
"tslib": "^1.11.1",
"typescript": "^3.8.3"
},
"dependencies": {
"fast-glob": "^3.2.2",
"gulp-if": "^3.0.0",
"next": "9.3.4",
"next-compose-plugins": "2.2.0",
"next-transpile-modules": "3.2.0",
"node-pty": "^0.9.0",
"null-loader": "^3.0.0",
"path-is-absolute": "^2.0.0",
"pirates": "^4.0.1",
"readable-stream": "^3.6.0",
"resolve-bin": "^0.4.0",
"through2": "^3.0.1",
"vinyl": "^2.2.0",
"vinyl-file": "^3.0.0",
"vinyl-fs": "^3.0.3"
},
"gitHead": "6719104cb3e78948e7f06aa948ff72bbb84cb682"
}

View File

@@ -0,0 +1,16 @@
const addHook = require('pirates').addHook
addHook(
function (code) {
const wrapCode =
'\n;if(_launchEditorFn) { module.exports = require("@blitzjs/server/register/launch-editor").enhance(_launchEditorFn); }'
return code.replace(/module\.exports\s?=/, 'const _launchEditorFn =') + wrapCode
},
{
exts: ['.js'],
matcher: function (filename) {
return filename.match(/launch-editor\/index/)
},
ignoreNodeModules: false,
},
)

View File

@@ -0,0 +1,34 @@
// NOTE: This is here to support deep linking into this package
// It would be nice to be able to write this code in typescript
// TODO: Incorporate this deep linked file into our TS build
const ManifestLoader = require('@blitzjs/server').ManifestLoader
const resolve = require('path').resolve
module.exports = {
enhance: function (launchEditor) {
return function (file, specifiedEditor, onErrorCallback) {
// This location needs to be driven from the config instead of cwd()
const manifestLocation = resolve(process.cwd(), '_manifest.json')
ManifestLoader.load(manifestLocation)
.then((manifest) => {
// extract filename
const [filename, row, col] = file.split(':')
const originalPath = manifest.getByValue(filename)
if (!originalPath) {
throw new Error('Manifest did not yeild original path from ' + filename)
}
// Use sourcemaps eventually
const editorAddress = [originalPath, row, col].join(':')
return launchEditor(editorAddress, specifiedEditor, onErrorCallback)
})
.catch((err) => {
console.error(err)
})
}
},
}

View File

@@ -0,0 +1,40 @@
import {resolve} from 'path'
import {synchronizeFiles} from './synchronizer'
import {move, remove, pathExists} from 'fs-extra'
import {ServerConfig, enhance} from './config'
import {nextBuild} from './next-utils'
export async function build(config: ServerConfig) {
const {
rootFolder,
buildFolder,
nextBin,
ignoredPaths,
manifestPath,
writeManifestFile,
includePaths,
} = await enhance(config)
await synchronizeFiles({
src: rootFolder,
dest: buildFolder,
watch: false,
manifestPath,
writeManifestFile,
ignoredPaths,
includePaths,
})
await nextBuild(nextBin, buildFolder)
const rootNextFolder = resolve(rootFolder, '.next')
const buildNextFolder = resolve(buildFolder, '.next')
if (await pathExists(rootNextFolder)) {
await remove(rootNextFolder)
}
if (await pathExists(buildNextFolder)) {
await move(buildNextFolder, rootNextFolder)
}
}

Some files were not shown because too many files have changed in this diff Show More