├── .gitattributes ├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug_report.yaml │ └── feature_request.yaml ├── PULL_REQUEST_TEMPLATE.md └── workflows │ ├── codeql-analysis.yaml │ ├── release.yaml │ ├── semantic-pr.yaml │ └── test.yaml ├── .gitignore ├── .npmrc ├── .tool-versions ├── .vscode ├── extensions.json └── settings.json ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── assets └── github-preview-svelte-adapter-firebase.png ├── package.json ├── pnpm-lock.yaml ├── src ├── files │ ├── entry.js │ ├── firebase-to-svelte-kit.js │ └── shims.js ├── index.d.ts ├── index.js └── utils.js └── tests ├── end-to-end ├── scaffold │ ├── .firebaserc │ ├── firebase.json │ ├── functions │ │ ├── .gitignore │ │ ├── index.js │ │ └── package.json │ └── svelte.config.js └── test.bash ├── integration ├── functions_single_site │ ├── .firebaserc │ ├── firebase.json │ ├── functions │ │ ├── .gitignore │ │ ├── index.js │ │ └── package.json │ └── svelte.config.js ├── integration-test.bash ├── nested_app_dirs │ ├── .firebaserc │ ├── app │ │ └── svelte.config.js │ ├── firebase.json │ └── functions │ │ ├── .gitignore │ │ ├── index.js │ │ └── package.json └── run_service_id │ ├── .firebaserc │ ├── firebase.json │ ├── functions │ ├── .gitignore │ ├── index.js │ └── package.json │ └── svelte.config.js └── unit ├── fixtures ├── failures │ ├── cf_invalid_function_name.json │ ├── cf_multi_site_no_target_match.json │ ├── cf_multi_site_requires_target.json │ ├── cf_site_missing_functions.json │ ├── cf_site_rewrite_mismatch.json │ ├── cr_invalid_region.json │ ├── cr_invalid_serviceId.json │ ├── cr_missing_serviceId.json │ ├── invalid.json │ ├── missing_hosting.json │ ├── site_empty_public.json │ ├── site_missing_public.json │ ├── site_missing_rewrite.json │ └── sites_missing_rewrites.json └── successes │ ├── cf_site.json │ ├── cf_sites_w_site.json │ ├── cf_sites_w_target.json │ ├── cr_site.json │ ├── cr_sites_w_site.json │ └── cr_sites_w_target.json └── src ├── files └── firebase-to-svelte-kit.test.js └── utils.test.js /.gitattributes: -------------------------------------------------------------------------------- 1 | ## GITATTRIBUTES FOR WEB PROJECTS 2 | # 3 | # These settings are for any web project. 4 | # 5 | # Details per file setting: 6 | # text These files should be normalized (i.e. convert CRLF to LF). 7 | # binary These files are binary and should be left untouched. 8 | # 9 | # Note that binary is a macro for -text -diff. 10 | ###################################################################### 11 | 12 | # Auto detect 13 | ## Handle line endings automatically for files detected as 14 | ## text and leave all files detected as binary untouched. 15 | ## This will handle all files NOT defined below. 16 | * text=auto 17 | 18 | # Source code 19 | *.bash text eol=lf 20 | *.bat text eol=crlf 21 | *.cmd text eol=crlf 22 | *.coffee text 23 | *.css text 24 | *.htm text diff=html 25 | *.html text diff=html 26 | *.inc text 27 | *.ini text 28 | *.js text 29 | *.json text 30 | *.jsx text 31 | *.less text 32 | *.ls text 33 | *.map text -diff 34 | *.od text 35 | *.onlydata text 36 | *.php text diff=php 37 | *.pl text 38 | *.ps1 text eol=crlf 39 | *.py text diff=python 40 | *.rb text diff=ruby 41 | *.sass text 42 | *.scm text 43 | *.scss text diff=css 44 | *.sh text eol=lf 45 | *.sql text 46 | *.styl text 47 | *.tag text 48 | *.ts text 49 | *.tsx text 50 | *.xml text 51 | *.xhtml text diff=html 52 | 53 | # Docker 54 | Dockerfile text 55 | 56 | # Documentation 57 | *.ipynb text 58 | *.markdown text 59 | *.md text 60 | *.mdwn text 61 | *.mdown text 62 | *.mkd text 63 | *.mkdn text 64 | *.mdtxt text 65 | *.mdtext text 66 | *.txt text 67 | AUTHORS text 68 | CHANGELOG text 69 | CHANGES text 70 | CONTRIBUTING text 71 | COPYING text 72 | copyright text 73 | *COPYRIGHT* text 74 | INSTALL text 75 | license text 76 | LICENSE text 77 | NEWS text 78 | readme text 79 | *README* text 80 | TODO text 81 | 82 | # Templates 83 | *.dot text 84 | *.ejs text 85 | *.haml text 86 | *.handlebars text 87 | *.hbs text 88 | *.hbt text 89 | *.jade text 90 | *.latte text 91 | *.mustache text 92 | *.njk text 93 | *.phtml text 94 | *.tmpl text 95 | *.tpl text 96 | *.twig text 97 | *.vue text 98 | 99 | # Configs 100 | *.cnf text 101 | *.conf text 102 | *.config text 103 | .editorconfig text 104 | .env text 105 | .gitattributes text 106 | .gitconfig text 107 | .htaccess text 108 | *.lock text -diff 109 | package-lock.json text -diff 110 | *.toml text 111 | *.yaml text 112 | *.yml text 113 | browserslist text 114 | Makefile text 115 | makefile text 116 | 117 | # Heroku 118 | Procfile text 119 | 120 | # Graphics 121 | *.ai binary 122 | *.bmp binary 123 | *.eps binary 124 | *.gif binary 125 | *.gifv binary 126 | *.ico binary 127 | *.jng binary 128 | *.jp2 binary 129 | *.jpg binary 130 | *.jpeg binary 131 | *.jpx binary 132 | *.jxr binary 133 | *.pdf binary 134 | *.png binary 135 | *.psb binary 136 | *.psd binary 137 | # SVG treated as an asset (binary) by default. 138 | *.svg text 139 | # If you want to treat it as binary, 140 | # use the following line instead. 141 | # *.svg binary 142 | *.svgz binary 143 | *.tif binary 144 | *.tiff binary 145 | *.wbmp binary 146 | *.webp binary 147 | 148 | # Audio 149 | *.kar binary 150 | *.m4a binary 151 | *.mid binary 152 | *.midi binary 153 | *.mp3 binary 154 | *.ogg binary 155 | *.ra binary 156 | 157 | # Video 158 | *.3gpp binary 159 | *.3gp binary 160 | *.as binary 161 | *.asf binary 162 | *.asx binary 163 | *.fla binary 164 | *.flv binary 165 | *.m4v binary 166 | *.mng binary 167 | *.mov binary 168 | *.mp4 binary 169 | *.mpeg binary 170 | *.mpg binary 171 | *.ogv binary 172 | *.swc binary 173 | *.swf binary 174 | *.webm binary 175 | 176 | # Archives 177 | *.7z binary 178 | *.gz binary 179 | *.jar binary 180 | *.rar binary 181 | *.tar binary 182 | *.zip binary 183 | 184 | # Fonts 185 | *.ttf binary 186 | *.eot binary 187 | *.otf binary 188 | *.woff binary 189 | *.woff2 binary 190 | 191 | # Executables 192 | *.exe binary 193 | *.pyc binary 194 | 195 | # RC files (like .babelrc or .eslintrc) 196 | *.*rc text 197 | 198 | # Ignore files (like .npmignore or .gitignore) 199 | *.*ignore text -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [jthegedus] 4 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.yaml: -------------------------------------------------------------------------------- 1 | name: Bug Report 2 | description: Create a report to help us improve 3 | title: "bug: " 4 | labels: [bug] 5 | body: 6 | - type: markdown 7 | attributes: 8 | value: | 9 | Thanks for taking the time to fill out this bug report! 10 | - type: textarea 11 | id: description 12 | attributes: 13 | label: Describe the Bug 14 | description: A clear and concise description of what the bug is. 15 | validations: 16 | required: true 17 | - type: textarea 18 | id: reproduction 19 | attributes: 20 | label: Steps to Reproduce 21 | description: Tell us what actions you performed before the issue occurred 22 | placeholder: | 23 | 1. Go to '...' 24 | 2. Click on '....' 25 | 3. Scroll down to '....' 26 | 4. See error 27 | validations: 28 | required: true 29 | - type: textarea 30 | id: expected 31 | attributes: 32 | label: Expected Behaviour 33 | description: Tell us what should have happened? 34 | validations: 35 | required: true 36 | - type: input 37 | id: svelte-adapter-firebase 38 | attributes: 39 | label: svelte-adapter-firebase version 40 | description: What version of `svelte-adapter-firebase` are you running? 41 | placeholder: 0.9.2 42 | validations: 43 | required: true 44 | - type: input 45 | id: sveltekit 46 | attributes: 47 | label: sveltejs/kit version 48 | description: What version of `@sveltejs/kit` are you running? 49 | placeholder: 1.0.0-next.128 50 | validations: 51 | required: true 52 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.yaml: -------------------------------------------------------------------------------- 1 | name: "Feature Request" 2 | description: Suggest an idea for this project 3 | title: "feat: " 4 | labels: [enhancement] 5 | body: 6 | - type: markdown 7 | attributes: 8 | value: | 9 | Thanks for taking the time to request this feature! 10 | - type: textarea 11 | id: problem 12 | attributes: 13 | label: Describe the problem 14 | description: Please provide a clear and concise description the problem this feature would solve. The more information you can provide here, the better. 15 | placeholder: I'm always frustrated when... 16 | validations: 17 | required: true 18 | - type: textarea 19 | id: solution 20 | attributes: 21 | label: Describe the proposed solution 22 | description: Please provide a clear and concise description of what you would like to happen. 23 | placeholder: I would like to see... 24 | validations: 25 | required: true 26 | - type: textarea 27 | id: alternatives 28 | attributes: 29 | label: Alternatives considered 30 | description: "Please provide a clear and concise description of any alternative solutions or features you've considered." 31 | validations: 32 | required: true 33 | - type: dropdown 34 | id: importance 35 | attributes: 36 | label: Importance 37 | description: How important is this feature to you? 38 | options: 39 | - would make my life easier 40 | - i cannot use `svelte-adapter-firebase` without it 41 | validations: 42 | required: true 43 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | 4 | 5 | Fixes: List issue numbers here 6 | 7 | ## Other Information 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yaml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [ main ] 17 | pull_request: 18 | # The branches below must be a subset of the branches above 19 | branches: [ main ] 20 | schedule: 21 | - cron: '35 4 * * 5' 22 | 23 | jobs: 24 | analyze: 25 | name: Analyze 26 | runs-on: ubuntu-latest 27 | 28 | strategy: 29 | fail-fast: false 30 | matrix: 31 | language: [ 'javascript' ] 32 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] 33 | # Learn more: 34 | # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed 35 | 36 | steps: 37 | - name: Checkout repository 38 | uses: actions/checkout@v2 39 | 40 | # Initializes the CodeQL tools for scanning. 41 | - name: Initialize CodeQL 42 | uses: github/codeql-action/init@v1 43 | with: 44 | languages: ${{ matrix.language }} 45 | # If you wish to specify custom queries, you can do so here or in a config file. 46 | # By default, queries listed here will override any specified in a config file. 47 | # Prefix the list here with "+" to use these queries and those in the config file. 48 | # queries: ./path/to/local/query, your-org/your-repo/queries@main 49 | 50 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 51 | # If this step fails, then you should remove it and run the build manually (see below) 52 | - name: Autobuild 53 | uses: github/codeql-action/autobuild@v1 54 | 55 | # ℹ️ Command-line programs to run using the OS shell. 56 | # 📚 https://git.io/JvXDl 57 | 58 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 59 | # and modify them (or add more) to build your code if your project 60 | # uses a compiled language 61 | 62 | #- run: | 63 | # make bootstrap 64 | # make release 65 | 66 | - name: Perform CodeQL Analysis 67 | uses: github/codeql-action/analyze@v1 68 | -------------------------------------------------------------------------------- /.github/workflows/release.yaml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | release: 10 | name: Release 11 | runs-on: ubuntu-18.04 12 | env: 13 | HUSKY: 0 14 | steps: 15 | - uses: GoogleCloudPlatform/release-please-action@v2 16 | id: release 17 | with: 18 | release-type: node 19 | bump-minor-pre-major: true # remove this to enable breaking changes causing 1.0.0 tag 20 | 21 | # The logic below handles the npm publication: 22 | # The if statements ensure that a publication only occurs when a new release is created 23 | - name: Checkout 24 | uses: actions/checkout@v2 25 | with: 26 | fetch-depth: 0 27 | persist-credentials: false 28 | if: ${{ steps.release.outputs.release_created }} 29 | 30 | - uses: actions/setup-node@v1 31 | with: 32 | node-version: 16 33 | registry-url: 'https://registry.npmjs.org' 34 | if: ${{ steps.release.outputs.release_created }} 35 | 36 | - run: npm install 37 | if: ${{ steps.release.outputs.release_created }} 38 | 39 | - run: npm publish 40 | env: 41 | NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}} 42 | if: ${{ steps.release.outputs.release_created }} 43 | -------------------------------------------------------------------------------- /.github/workflows/semantic-pr.yaml: -------------------------------------------------------------------------------- 1 | name: Lint PR 2 | 3 | on: 4 | pull_request_target: 5 | types: 6 | - opened 7 | - edited 8 | - synchronize 9 | 10 | jobs: 11 | main: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: amannn/action-semantic-pull-request@v3.4.0 15 | env: 16 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 17 | with: 18 | validateSingleCommit: true 19 | -------------------------------------------------------------------------------- /.github/workflows/test.yaml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | 9 | env: 10 | CI: true 11 | FIREBASE_EMULATORS_PATH: ${{ github.workspace }}/emulator-cache 12 | 13 | jobs: 14 | unit: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@v3 18 | - name: install asdf 19 | uses: asdf-vm/actions/install@v1 20 | - run: pnpm install 21 | - run: pnpm run test 22 | 23 | integration: 24 | runs-on: ubuntu-latest 25 | needs: 26 | - unit 27 | strategy: 28 | fail-fast: true 29 | matrix: 30 | params: 31 | - { 32 | test_dir: "functions_single_site", 33 | validation_app_dir: "public/_app/immutable/components/pages/_page.svelte-*.js", 34 | validation_compute_dir: "functions/sveltekit/index.js", 35 | nested_dir: ".", 36 | } 37 | - { 38 | test_dir: "nested_app_dirs", 39 | validation_app_dir: "public/_app/immutable/components/pages/_page.svelte-*.js", 40 | validation_compute_dir: "functions/sveltekit/index.js", 41 | nested_dir: "app", 42 | } 43 | - { 44 | test_dir: "run_service_id", 45 | validation_app_dir: "public/_app/immutable/components/pages/_page.svelte-*.js", 46 | validation_compute_dir: "functions/cloudrun/index.js", 47 | nested_dir: ".", 48 | } 49 | steps: 50 | - uses: actions/checkout@v3 51 | - name: install asdf 52 | uses: asdf-vm/actions/install@v1 53 | - name: Run Integration test for ${{ matrix.params.test_dir }} 54 | run: | 55 | bash ./tests/integration/integration-test.bash ${{ matrix.params.test_dir }} ${{ matrix.params.validation_app_dir }} ${{ matrix.params.validation_compute_dir }} ${{ matrix.params.nested_dir }} 56 | 57 | end-to-end: 58 | runs-on: ubuntu-latest 59 | needs: 60 | - unit 61 | steps: 62 | - uses: actions/checkout@v3 63 | - name: install asdf 64 | uses: asdf-vm/actions/install@v1 65 | - name: Cache firebase emulators 66 | uses: actions/cache@v2 67 | with: 68 | path: ${{ env.FIREBASE_EMULATORS_PATH }} 69 | key: ${{ runner.os }}-firebase-emulators-${{ hashFiles('emulator-cache/**') }} 70 | continue-on-error: true 71 | - name: Run end-to-end test script 72 | run: | 73 | bash ./tests/end-to-end/test.bash 74 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.toptal.com/developers/gitignore/api/node 3 | # Edit at https://www.toptal.com/developers/gitignore?templates=node 4 | 5 | ### Node ### 6 | # Logs 7 | logs 8 | *.log 9 | npm-debug.log* 10 | yarn-debug.log* 11 | yarn-error.log* 12 | lerna-debug.log* 13 | 14 | # Diagnostic reports (https://nodejs.org/api/report.html) 15 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 16 | 17 | # Runtime data 18 | pids 19 | *.pid 20 | *.seed 21 | *.pid.lock 22 | 23 | # Directory for instrumented libs generated by jscoverage/JSCover 24 | lib-cov 25 | 26 | # Coverage directory used by tools like istanbul 27 | coverage 28 | *.lcov 29 | 30 | # nyc test coverage 31 | .nyc_output 32 | 33 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 34 | .grunt 35 | 36 | # Bower dependency directory (https://bower.io/) 37 | bower_components 38 | 39 | # node-waf configuration 40 | .lock-wscript 41 | 42 | # Compiled binary addons (https://nodejs.org/api/addons.html) 43 | build/Release 44 | 45 | # Dependency directories 46 | node_modules/ 47 | jspm_packages/ 48 | 49 | # TypeScript v1 declaration files 50 | typings/ 51 | 52 | # TypeScript cache 53 | *.tsbuildinfo 54 | 55 | # Optional npm cache directory 56 | .npm 57 | 58 | # Optional eslint cache 59 | .eslintcache 60 | 61 | # Optional stylelint cache 62 | .stylelintcache 63 | 64 | # Microbundle cache 65 | .rpt2_cache/ 66 | .rts2_cache_cjs/ 67 | .rts2_cache_es/ 68 | .rts2_cache_umd/ 69 | 70 | # Optional REPL history 71 | .node_repl_history 72 | 73 | # Output of 'npm pack' 74 | *.tgz 75 | 76 | # Yarn Integrity file 77 | .yarn-integrity 78 | 79 | # dotenv environment variables file 80 | .env 81 | .env.test 82 | .env*.local 83 | 84 | # parcel-bundler cache (https://parceljs.org/) 85 | .cache 86 | .parcel-cache 87 | 88 | # Next.js build output 89 | .next 90 | 91 | # Nuxt.js build / generate output 92 | .nuxt 93 | dist 94 | 95 | # Storybook build outputs 96 | .out 97 | .storybook-out 98 | storybook-static 99 | 100 | # rollup.js default build output 101 | dist/ 102 | 103 | # Gatsby files 104 | .cache/ 105 | # Comment in the public line in if your project uses Gatsby and not Next.js 106 | # https://nextjs.org/blog/next-9-1#public-directory-support 107 | # public 108 | 109 | # vuepress build output 110 | .vuepress/dist 111 | 112 | # Serverless directories 113 | .serverless/ 114 | 115 | # FuseBox cache 116 | .fusebox/ 117 | 118 | # DynamoDB Local files 119 | .dynamodb/ 120 | 121 | # TernJS port file 122 | .tern-port 123 | 124 | # Stores VSCode versions used for testing VSCode extensions 125 | .vscode-test 126 | 127 | # Temporary folders 128 | tmp/ 129 | temp/ 130 | 131 | # End of https://www.toptal.com/developers/gitignore/api/node 132 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | auto-install-peers=true 2 | -------------------------------------------------------------------------------- /.tool-versions: -------------------------------------------------------------------------------- 1 | firebase 11.19.0 2 | nodejs 18.12.1 3 | pnpm 7.18.2 4 | 5 | # use for Firebase Emulator 6 | 7 | java openjdk-18.0.1.1 8 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "redhat.vscode-yaml", 4 | "samverschueren.linter-xo" 5 | ] 6 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "xo.enable": true, 3 | "xo.format.enable": true, 4 | "files.insertFinalNewline": true, 5 | "[javascript]": { 6 | "editor.defaultFormatter": "samverschueren.linter-xo" 7 | }, 8 | "[typescript]": { 9 | "editor.defaultFormatter": "samverschueren.linter-xo" 10 | }, 11 | "[json]": { 12 | "editor.defaultFormatter": "vscode.json-language-features", 13 | }, 14 | "[yaml]": { 15 | "editor.defaultFormatter": "redhat.vscode-yaml" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [0.15.0](https://www.github.com/jthegedus/svelte-adapter-firebase/compare/v0.14.4...v0.15.0) (2023-01-17) 4 | 5 | 6 | ### ⚠ BREAKING CHANGES 7 | 8 | * sync with kit@1.1.1 based on (PRs #194 #200) (#203) 9 | 10 | ### Bug Fixes 11 | 12 | * **#192:** replace getStaticDirectory with kit.files.assets ([#193](https://www.github.com/jthegedus/svelte-adapter-firebase/issues/193)) ([a4b4daa](https://www.github.com/jthegedus/svelte-adapter-firebase/commit/a4b4daac795cd93a4a06f5d218f69841810e686b)) 13 | * sync with kit@1.1.1 based on (PRs [#194](https://www.github.com/jthegedus/svelte-adapter-firebase/issues/194) [#200](https://www.github.com/jthegedus/svelte-adapter-firebase/issues/200)) ([#203](https://www.github.com/jthegedus/svelte-adapter-firebase/issues/203)) ([352072d](https://www.github.com/jthegedus/svelte-adapter-firebase/commit/352072d0f30ef76442fc143ccb4a446119a573ee)) 14 | 15 | ### [0.14.4](https://www.github.com/jthegedus/svelte-adapter-firebase/compare/v0.14.3...v0.14.4) (2022-09-08) 16 | 17 | 18 | ### Bug Fixes 19 | 20 | * cross-site POST requests ([#184](https://www.github.com/jthegedus/svelte-adapter-firebase/issues/184)) ([e2d8081](https://www.github.com/jthegedus/svelte-adapter-firebase/commit/e2d80812976feb5d16e34131462628647af59dc3)) 21 | 22 | ### [0.14.3](https://www.github.com/jthegedus/svelte-adapter-firebase/compare/v0.14.2...v0.14.3) (2022-09-02) 23 | 24 | 25 | ### Bug Fixes 26 | 27 | * **#180:** Prepend `Server.init` before calling `Server.respond` ([#181](https://www.github.com/jthegedus/svelte-adapter-firebase/issues/181)) ([5c937c9](https://www.github.com/jthegedus/svelte-adapter-firebase/commit/5c937c9ca6627025ecd19c889a9be3b50be2a266)) 28 | 29 | ### [0.14.2](https://www.github.com/jthegedus/svelte-adapter-firebase/compare/v0.14.1...v0.14.2) (2022-08-13) 30 | 31 | 32 | ### Bug Fixes 33 | 34 | * **#172:** Remove reference to obsoleted `writeStatic` ([#173](https://www.github.com/jthegedus/svelte-adapter-firebase/issues/173)) ([9a990a0](https://www.github.com/jthegedus/svelte-adapter-firebase/commit/9a990a00667b5f2197915d8b380593a5a05d306b)) 35 | * bump kit versions, update deps & fix xojs issues ([#178](https://www.github.com/jthegedus/svelte-adapter-firebase/issues/178)) ([dde0a0e](https://www.github.com/jthegedus/svelte-adapter-firebase/commit/dde0a0ea9802000df2b07c760357d8eac8a976fa)) 36 | * Pass rendered.headers to writeHead as object ([#176](https://www.github.com/jthegedus/svelte-adapter-firebase/issues/176)) ([11d90cb](https://www.github.com/jthegedus/svelte-adapter-firebase/commit/11d90cb6059585e9c95ca06d06e153ddf514f7b4)) 37 | 38 | ### [0.14.1](https://www.github.com/jthegedus/svelte-adapter-firebase/compare/v0.14.0...v0.14.1) (2022-06-15) 39 | 40 | 41 | ### Bug Fixes 42 | 43 | * **#169:** Replace `installFetch()` with `installPolyfills()` ([#170](https://www.github.com/jthegedus/svelte-adapter-firebase/issues/170)) ([9c44f0c](https://www.github.com/jthegedus/svelte-adapter-firebase/commit/9c44f0cbcab64a390ad4197f8a13f474761e5476)) 44 | * builds on windows ([#164](https://www.github.com/jthegedus/svelte-adapter-firebase/issues/164)) ([0dd5005](https://www.github.com/jthegedus/svelte-adapter-firebase/commit/0dd50055706e85d4fb1cff562ab73af42c93df33)) 45 | 46 | ### [0.14.1](https://www.github.com/pham/svelte-adapter-firebase/compare/v0.14.0...v0.14.1) (2022-06-02) 47 | 48 | 49 | ### Bug Fixes 50 | 51 | * **#169:** Replace @sveltejs/kit/install-fetch with @sveltejs/kit/node/polyfills ([cc00699](https://www.github.com/pham/svelte-adapter-firebase/commit/cc00699fdf968a6a771a644ea75cc25877ed9dc2)) 52 | 53 | ## [0.14.0](https://www.github.com/jthegedus/svelte-adapter-firebase/compare/v0.13.1...v0.14.0) (2022-05-14) 54 | 55 | 56 | ### Features 57 | 58 | * Support for SvelteKit Next 303 ([#159](https://www.github.com/jthegedus/svelte-adapter-firebase/issues/159)) ([d10fcf6](https://www.github.com/jthegedus/svelte-adapter-firebase/commit/d10fcf6b68b65f59363aa9d25210b0f9f2b63793)) 59 | * upgrade to sveltekit 329 ([#161](https://www.github.com/jthegedus/svelte-adapter-firebase/issues/161)) ([bf7c2e9](https://www.github.com/jthegedus/svelte-adapter-firebase/commit/bf7c2e90e61f7562993d465bc90b4ab1232dfaac)) 60 | 61 | 62 | ### Bug Fixes 63 | 64 | * reading 'log' error ([#157](https://www.github.com/jthegedus/svelte-adapter-firebase/issues/157)) ([f855bfb](https://www.github.com/jthegedus/svelte-adapter-firebase/commit/f855bfb7a18ed9186463e8a04af8e4ef886d3b6a)) 65 | 66 | ## [0.14.0](https://www.github.com/jthegedus/svelte-adapter-firebase/compare/v0.13.1...v0.14.0) (2022-05-13) 67 | 68 | ### Bug Fixes 69 | 70 | - Supports the latest sveltekit apis (tested up until sveltekit 330) ([[#159](https://github.com/jthegedus/svelte-adapter-firebase/issues/159)) 71 | - Thanks to nielsvandermolen and co3k for the patch submissions. 72 | 73 | ### [0.13.1](https://www.github.com/jthegedus/svelte-adapter-firebase/compare/v0.13.0...v0.13.1) (2021-10-10) 74 | 75 | ### Bug Fixes 76 | 77 | - missed rename of hostingSite to target, bump kit for types ([#149](https://www.github.com/jthegedus/svelte-adapter-firebase/issues/149)) ([cebf821](https://www.github.com/jthegedus/svelte-adapter-firebase/commit/cebf8210c22291967b8ee2cbf3511736ddfbaef1)) 78 | 79 | ## [0.13.0](https://www.github.com/jthegedus/svelte-adapter-firebase/compare/v0.12.0...v0.13.0) (2021-09-19) 80 | 81 | ### Features 82 | 83 | - fb deploy targets. Rename opt `hostingSite` to `target` ([#144](https://www.github.com/jthegedus/svelte-adapter-firebase/issues/144)) ([2ffe777](https://www.github.com/jthegedus/svelte-adapter-firebase/commit/2ffe7774d41de9d4f88dc7d7935e5397e5999b44)) 84 | 85 | ### Bug Fixes 86 | 87 | - e2e tests with latest emulator ([#140](https://www.github.com/jthegedus/svelte-adapter-firebase/issues/140)) ([5227bb5](https://www.github.com/jthegedus/svelte-adapter-firebase/commit/5227bb5ced4576b530d0b9e6c38f91920e9863fe)) 88 | - pass null if no req.rawBody ([#143](https://www.github.com/jthegedus/svelte-adapter-firebase/issues/143)) ([15c9ca4](https://www.github.com/jthegedus/svelte-adapter-firebase/commit/15c9ca4bbd6783ab43614cb6b49692e4685372b8)) 89 | 90 | ## [0.12.0](https://www.github.com/jthegedus/svelte-adapter-firebase/compare/v0.11.2...v0.12.0) (2021-09-16) 91 | 92 | ### Features 93 | 94 | - compute esbuild target from Function runtime version ([#137](https://www.github.com/jthegedus/svelte-adapter-firebase/issues/137)) ([61f2b3b](https://www.github.com/jthegedus/svelte-adapter-firebase/commit/61f2b3bbc4c3c008a48b38890e4424956f06f9d9)) 95 | - deprecate Cloud Run support ([#135](https://www.github.com/jthegedus/svelte-adapter-firebase/issues/135)) ([ee5d92a](https://www.github.com/jthegedus/svelte-adapter-firebase/commit/ee5d92a2cf19508959846ef9090f679773d299de)) 96 | - rename adapter config `firebaseJson` to `firebaseJsonPath` ([#135](https://www.github.com/jthegedus/svelte-adapter-firebase/issues/135)) ([ee5d92a](https://www.github.com/jthegedus/svelte-adapter-firebase/commit/ee5d92a2cf19508959846ef9090f679773d299de)) 97 | 98 | ### Bug Fixes 99 | 100 | - output entrypoint code without verbose mode ([#138](https://www.github.com/jthegedus/svelte-adapter-firebase/issues/138)) ([305ae73](https://www.github.com/jthegedus/svelte-adapter-firebase/commit/305ae73197dc943dfe05734487ba43fd72e5a11b)) 101 | 102 | ### [0.11.2](https://www.github.com/jthegedus/svelte-adapter-firebase/compare/v0.11.1...v0.11.2) (2021-09-10) 103 | 104 | ### Bug Fixes 105 | 106 | - export package.json from package ([#132](https://www.github.com/jthegedus/svelte-adapter-firebase/issues/132)) ([62e29f6](https://www.github.com/jthegedus/svelte-adapter-firebase/commit/62e29f6b8a646cfb9537ed94f60cf040c843ed6e)) 107 | 108 | ### [0.11.1](https://www.github.com/jthegedus/svelte-adapter-firebase/compare/v0.11.0...v0.11.1) (2021-09-08) 109 | 110 | ### Bug Fixes 111 | 112 | - support nodejs16 env ([#129](https://www.github.com/jthegedus/svelte-adapter-firebase/issues/129)) ([3645519](https://www.github.com/jthegedus/svelte-adapter-firebase/commit/36455197594c49bed10b286a3fd7f35a0eb951f1)) 113 | 114 | ## [0.11.0](https://www.github.com/jthegedus/svelte-adapter-firebase/compare/v0.10.5...v0.11.0) (2021-08-23) 115 | 116 | ### Features 117 | 118 | - new rawBody type on SvelteKit req ([#123](https://www.github.com/jthegedus/svelte-adapter-firebase/issues/123)) ([fec3174](https://www.github.com/jthegedus/svelte-adapter-firebase/commit/fec31742415274110a1b068915cd166029dec6ad)) 119 | 120 | ### [0.10.5](https://www.github.com/jthegedus/svelte-adapter-firebase/compare/v0.10.4...v0.10.5) (2021-07-21) 121 | 122 | ### Bug Fixes 123 | 124 | - document potential Cloud Run deprecation ([#119](https://www.github.com/jthegedus/svelte-adapter-firebase/issues/119)) ([ac8cd74](https://www.github.com/jthegedus/svelte-adapter-firebase/commit/ac8cd745632f4d0f63d116dd6ea0aedf4d7178ab)) 125 | 126 | ### [0.10.4](https://www.github.com/jthegedus/svelte-adapter-firebase/compare/v0.10.3...v0.10.4) (2021-07-21) 127 | 128 | ### Features 129 | 130 | - support esbuild config ([#109](https://www.github.com/jthegedus/svelte-adapter-firebase/issues/109)) ([3b7c733](https://www.github.com/jthegedus/svelte-adapter-firebase/commit/3b7c733249b6f29e4041e56bbc9c8dabd24c9c86)) 131 | 132 | ### Bug Fixes 133 | 134 | - update sveltekit peer dep ([#112](https://www.github.com/jthegedus/svelte-adapter-firebase/issues/112)) ([d6aea82](https://www.github.com/jthegedus/svelte-adapter-firebase/commit/d6aea8293b04d6e529daed24d420656317315aed)) 135 | 136 | ## [0.9.2](https://github.com/jthegedus/svelte-adapter-firebase/compare/v0.9.1...v0.9.2) (2021-07-09) 137 | 138 | ### Bug Fixes 139 | 140 | - beta version compat table ([114a30f](https://github.com/jthegedus/svelte-adapter-firebase/commit/114a30f0e95bf34b5ecc58457bc920d8e15410aa)) 141 | 142 | ## [0.9.1](https://github.com/jthegedus/svelte-adapter-firebase/compare/v0.9.0...v0.9.1) (2021-07-07) 143 | 144 | ### Bug Fixes 145 | 146 | - use esbuild inject api to ensure exec order & polyfill ([#104](https://github.com/jthegedus/svelte-adapter-firebase/issues/104)) ([52429d2](https://github.com/jthegedus/svelte-adapter-firebase/commit/52429d23809c08dd8027e6b0ce8c2e04b6b41136)) 147 | 148 | # [0.9.0](https://github.com/jthegedus/svelte-adapter-firebase/compare/v0.8.5...v0.9.0) (2021-07-04) 149 | 150 | ### Features 151 | 152 | - adapter runs app init ([#103](https://github.com/jthegedus/svelte-adapter-firebase/issues/103)) ([9d281fb](https://github.com/jthegedus/svelte-adapter-firebase/commit/9d281fb0dfc9b41146232768850b67fa1b866e84)) 153 | 154 | ## [0.8.5](https://github.com/jthegedus/svelte-adapter-firebase/compare/v0.8.4...v0.8.5) (2021-06-25) 155 | 156 | ### Bug Fixes 157 | 158 | - valid cloud run region ([#102](https://github.com/jthegedus/svelte-adapter-firebase/issues/102)) ([58d8d3d](https://github.com/jthegedus/svelte-adapter-firebase/commit/58d8d3dfec5ac9d644df63fd00e497ac21544a91)) 159 | 160 | ## [0.8.4](https://github.com/jthegedus/svelte-adapter-firebase/compare/v0.8.3...v0.8.4) (2021-05-30) 161 | 162 | ### Bug Fixes 163 | 164 | - specify minimum nodejs version in package.json ([#98](https://github.com/jthegedus/svelte-adapter-firebase/issues/98)) ([456da34](https://github.com/jthegedus/svelte-adapter-firebase/commit/456da349d359470002dadb841acce5cbb0b8589a)) 165 | 166 | ## [0.8.3](https://github.com/jthegedus/svelte-adapter-firebase/compare/v0.8.2...v0.8.3) (2021-05-30) 167 | 168 | ### Bug Fixes 169 | 170 | - use SK computed config to determine if static dirs differ ([#97](https://github.com/jthegedus/svelte-adapter-firebase/issues/97)) ([53e3c97](https://github.com/jthegedus/svelte-adapter-firebase/commit/53e3c97d321fa8a29dfa44059927291893d4ee2a)) 171 | 172 | ## [0.8.2](https://github.com/jthegedus/svelte-adapter-firebase/compare/v0.8.1...v0.8.2) (2021-05-30) 173 | 174 | ### Bug Fixes 175 | 176 | - simplify body parsing & add handler types ([#96](https://github.com/jthegedus/svelte-adapter-firebase/issues/96)) ([8b47baf](https://github.com/jthegedus/svelte-adapter-firebase/commit/8b47baf262dcf409db83eaeb1fd84280b18c663f)) 177 | 178 | ## [0.8.1](https://github.com/jthegedus/svelte-adapter-firebase/compare/v0.8.0...v0.8.1) (2021-05-30) 179 | 180 | ### Bug Fixes 181 | 182 | - wait request body infinitely in handler.js ([#93](https://github.com/jthegedus/svelte-adapter-firebase/issues/93)) ([94c4158](https://github.com/jthegedus/svelte-adapter-firebase/commit/94c4158ee0fd11194e300f0e2b81b66f16b3a15d)) 183 | 184 | # [0.8.0](https://github.com/jthegedus/svelte-adapter-firebase/compare/v0.7.8...v0.8.0) (2021-05-30) 185 | 186 | ### Features 187 | 188 | - update adapter api to latest with config ([#95](https://github.com/jthegedus/svelte-adapter-firebase/issues/95)) ([622739f](https://github.com/jthegedus/svelte-adapter-firebase/commit/622739fea6628c80e09453944171f690c0c41dc7)) 189 | 190 | ## [0.7.8](https://github.com/jthegedus/svelte-adapter-firebase/compare/v0.7.7...v0.7.8) (2021-05-28) 191 | 192 | ### Bug Fixes 193 | 194 | - improve tests & document tip codes ([#85](https://github.com/jthegedus/svelte-adapter-firebase/issues/85)) ([8f33de7](https://github.com/jthegedus/svelte-adapter-firebase/commit/8f33de7e3e542272ddba2cbc85903ab4ae02492f)) 195 | 196 | ## [0.7.7](https://github.com/jthegedus/svelte-adapter-firebase/compare/v0.7.6...v0.7.7) (2021-05-17) 197 | 198 | ### Bug Fixes 199 | 200 | - improve guide & logging of adapter ([#80](https://github.com/jthegedus/svelte-adapter-firebase/issues/80)) ([e60f5e2](https://github.com/jthegedus/svelte-adapter-firebase/commit/e60f5e2241f4c590267d2a1da0e474e8aa214650)) 201 | 202 | ## [0.7.6](https://github.com/jthegedus/svelte-adapter-firebase/compare/v0.7.5...v0.7.6) (2021-05-16) 203 | 204 | ### Bug Fixes 205 | 206 | - import of svelte.config.js requires string ([#74](https://github.com/jthegedus/svelte-adapter-firebase/issues/74)) ([3376a22](https://github.com/jthegedus/svelte-adapter-firebase/commit/3376a2250e64f79443a47787b206a8eb4485afe2)) 207 | 208 | ## [0.7.5](https://github.com/jthegedus/svelte-adapter-firebase/compare/v0.7.4...v0.7.5) (2021-05-16) 209 | 210 | ### Bug Fixes 211 | 212 | - import call for svelte.config.js on windows ([#76](https://github.com/jthegedus/svelte-adapter-firebase/issues/76)) ([02992ce](https://github.com/jthegedus/svelte-adapter-firebase/commit/02992cebfaa7681200a1773d00f87b4e3be00583)) 213 | 214 | ## [0.7.4](https://github.com/jthegedus/svelte-adapter-firebase/compare/v0.7.3...v0.7.4) (2021-05-13) 215 | 216 | ### Bug Fixes 217 | 218 | - instruct firebase deployment alongside Cloud Run ([#67](https://github.com/jthegedus/svelte-adapter-firebase/issues/67)) ([d1a1797](https://github.com/jthegedus/svelte-adapter-firebase/commit/d1a17977085f97fa20f25e5fc7e583ac1ec66b93)) 219 | 220 | ## [0.7.3](https://github.com/jthegedus/svelte-adapter-firebase/compare/v0.7.2...v0.7.3) (2021-05-13) 221 | 222 | ### Bug Fixes 223 | 224 | - ensure valid cloud function runtime version ([#66](https://github.com/jthegedus/svelte-adapter-firebase/issues/66)) ([46fafad](https://github.com/jthegedus/svelte-adapter-firebase/commit/46fafadb6ccfe0c39f0eeb3ee43bdebb4c13235c)) 225 | 226 | ## [0.7.2](https://github.com/jthegedus/svelte-adapter-firebase/compare/v0.7.1...v0.7.2) (2021-05-13) 227 | 228 | ### Bug Fixes 229 | 230 | - static output dir differes from static source dirs ([#65](https://github.com/jthegedus/svelte-adapter-firebase/issues/65)) ([72c7670](https://github.com/jthegedus/svelte-adapter-firebase/commit/72c7670e0c035d6c84f0361b5d39b3082a67958a)) 231 | 232 | ## [0.7.1](https://github.com/jthegedus/svelte-adapter-firebase/compare/v0.7.0...v0.7.1) (2021-05-13) 233 | 234 | ### Bug Fixes 235 | 236 | - github release assets ([#63](https://github.com/jthegedus/svelte-adapter-firebase/issues/63)) ([cd1a5e0](https://github.com/jthegedus/svelte-adapter-firebase/commit/cd1a5e04d19aed5aaf57c0303418eeb11dc30981)) 237 | 238 | # [0.7.0](https://github.com/jthegedus/svelte-adapter-firebase/compare/v0.6.5...v0.7.0) (2021-05-12) 239 | 240 | ### Features 241 | 242 | - sync new SvelteKit & init test suite ([#52](https://github.com/jthegedus/svelte-adapter-firebase/issues/52)) ([a1cb743](https://github.com/jthegedus/svelte-adapter-firebase/commit/a1cb743e835b6782344437026b0a4c238bb39842)) 243 | 244 | ## [0.6.5](https://github.com/jthegedus/svelte-adapter-firebase/compare/v0.6.4...v0.6.5) (2021-05-10) 245 | 246 | ### Bug Fixes 247 | 248 | - resolve dirname of firebaseJsonDir ([#49](https://github.com/jthegedus/svelte-adapter-firebase/issues/49)) ([7556c7d](https://github.com/jthegedus/svelte-adapter-firebase/commit/7556c7d21f52488f500498a7bbf79f82771f51b2)) 249 | 250 | ## [0.6.4](https://github.com/jthegedus/svelte-adapter-firebase/compare/v0.6.3...v0.6.4) (2021-05-10) 251 | 252 | ### Bug Fixes 253 | 254 | - export firebaseJsonDir from config parser func ([#48](https://github.com/jthegedus/svelte-adapter-firebase/issues/48)) ([5ec2e18](https://github.com/jthegedus/svelte-adapter-firebase/commit/5ec2e184669a036941e5ad48477b93aa67ffbf85)) 255 | 256 | ## [0.6.3](https://github.com/jthegedus/svelte-adapter-firebase/compare/v0.6.2...v0.6.3) (2021-05-07) 257 | 258 | ### Bug Fixes 259 | 260 | - allow any node package manager to install ([#46](https://github.com/jthegedus/svelte-adapter-firebase/issues/46)) ([417f167](https://github.com/jthegedus/svelte-adapter-firebase/commit/417f1673fd3561d3cd80d9a23944407a8485fd7f)) 261 | 262 | ## [0.6.2](https://github.com/jthegedus/svelte-adapter-firebase/compare/v0.6.1...v0.6.2) (2021-05-07) 263 | 264 | ### Bug Fixes 265 | 266 | - treat `firebase.json` as root dir for Firebase resources ([#43](https://github.com/jthegedus/svelte-adapter-firebase/issues/43)) ([82c8493](https://github.com/jthegedus/svelte-adapter-firebase/commit/82c8493403199b82645eed8138fe04e79a108453)) 267 | 268 | ## [0.6.1](https://github.com/jthegedus/svelte-adapter-firebase/compare/v0.6.0...v0.6.1) (2021-05-06) 269 | 270 | ### Bug Fixes 271 | 272 | - handle empty deps when cloud run target ([#41](https://github.com/jthegedus/svelte-adapter-firebase/issues/41)) ([b7719bd](https://github.com/jthegedus/svelte-adapter-firebase/commit/b7719bd486de71ceccdc57c5703de49cdb3f9f77)) 273 | 274 | # [0.6.0](https://github.com/jthegedus/svelte-adapter-firebase/compare/v0.5.2...v0.6.0) (2021-05-06) 275 | 276 | ### Bug Fixes 277 | 278 | - remove unused pnpm run build cmd from workflow ([#40](https://github.com/jthegedus/svelte-adapter-firebase/issues/40)) ([e7ab34a](https://github.com/jthegedus/svelte-adapter-firebase/commit/e7ab34a2ac3da094f006e76e2b327cfe0463d547)) 279 | 280 | ### Features 281 | 282 | - update to kit@next.100, convert to esm ([#39](https://github.com/jthegedus/svelte-adapter-firebase/issues/39)) ([d2f95a1](https://github.com/jthegedus/svelte-adapter-firebase/commit/d2f95a1132abee67b41dce5a9419f132ea3164ce)) 283 | 284 | ## [0.5.2](https://github.com/jthegedus/svelte-adapter-firebase/compare/v0.5.1...v0.5.2) (2021-04-28) 285 | 286 | ### Bug Fixes 287 | 288 | - use searchParams in handler.js ([#35](https://github.com/jthegedus/svelte-adapter-firebase/issues/35)) ([b2911a0](https://github.com/jthegedus/svelte-adapter-firebase/commit/b2911a0e713f7da0371ebb8791dbbefb9875096b)) 289 | 290 | ## [0.5.1](https://github.com/jthegedus/svelte-adapter-firebase/compare/v0.5.0...v0.5.1) (2021-03-22) 291 | 292 | ### Bug Fixes 293 | 294 | - align adapter API with SvelteKit v1.0.0-next.54 ([#23](https://github.com/jthegedus/svelte-adapter-firebase/issues/23)) ([abe68f1](https://github.com/jthegedus/svelte-adapter-firebase/commit/abe68f19a293758574893984ebbb0c36b0a448ae)) 295 | 296 | # [0.5.0](https://github.com/jthegedus/svelte-adapter-firebase/compare/v0.4.1...v0.5.0) (2021-03-16) 297 | 298 | ### Features 299 | 300 | - cloud function support ([#21](https://github.com/jthegedus/svelte-adapter-firebase/issues/21)) ([2437374](https://github.com/jthegedus/svelte-adapter-firebase/commit/2437374b5b3517f6183e5d7ad8b5f5fe448ed61f)) 301 | 302 | ## [0.4.1](https://github.com/jthegedus/svelte-adapter-firebase/compare/v0.4.0...v0.4.1) (2021-03-15) 303 | 304 | ### Bug Fixes 305 | 306 | - dir structure and rollup cli entrypoint ([#19](https://github.com/jthegedus/svelte-adapter-firebase/issues/19)) ([fd0c5f2](https://github.com/jthegedus/svelte-adapter-firebase/commit/fd0c5f2712445edf4f9aa91820b53098bd1cab88)) 307 | 308 | # [0.4.0](https://github.com/jthegedus/svelte-adapter-firebase/compare/v0.3.3...v0.4.0) (2021-03-15) 309 | 310 | ### Features 311 | 312 | - SvelteKit CR update with better Firebase config parsing ([#18](https://github.com/jthegedus/svelte-adapter-firebase/issues/18)) ([42ddb0c](https://github.com/jthegedus/svelte-adapter-firebase/commit/42ddb0c5f5da6472c6a74786a14b1730ceefec58)) 313 | 314 | ## [0.3.3](https://github.com/jthegedus/svelte-adapter-firebase/compare/v0.3.2...v0.3.3) (2021-03-04) 315 | 316 | ### Bug Fixes 317 | 318 | - inline svelte-app-utils lib ([#15](https://github.com/jthegedus/svelte-adapter-firebase/issues/15)) ([6f79964](https://github.com/jthegedus/svelte-adapter-firebase/commit/6f79964715cda538a72f9a12f50a8584953db8d1)) 319 | 320 | # Changelog 321 | 322 | All notable changes to this project will be documented in this file. 323 | 324 | ### [0.3.2](https://github.com/jthegedus/svelte-adapter-firebase/compare/v0.3.1...v0.3.2) (2021-02-09) 325 | 326 | ### Bug Fixes 327 | 328 | - destructure of adapter config error ([#11](https://github.com/jthegedus/svelte-adapter-firebase/issues/11)) ([6ece557](https://github.com/jthegedus/svelte-adapter-firebase/commit/6ece5578129ff9178030b561850eacc5cf9af286)) 329 | 330 | ### [0.3.1](https://github.com/jthegedus/svelte-adapter-firebase/compare/v0.3.0...v0.3.1) (2021-02-08) 331 | 332 | ### Bug Fixes 333 | 334 | - elevate logs to .warn & check CFs for `@sveltejs/kit` dep ([#9](https://github.com/jthegedus/svelte-adapter-firebase/issues/9)) ([6b35ee1](https://github.com/jthegedus/svelte-adapter-firebase/commit/6b35ee1a711a979fedaf7f97fc8f513974599698)) 335 | 336 | ## 0.3.0 (2021-02-07) 337 | 338 | - fix: cjs output from sveltekit requires rename of local require in handler.js 339 | - fix: destructure of undefined in index.js.adapter(). Fixes #5 340 | 341 | ## 0.2.0 (2021-01-06) 342 | 343 | - chore: build with microbundle instead of Rollup directly 344 | - chore: format & lint with xojs instead of rome.tools 345 | - chore: Joi as dependency and not inlined 346 | 347 | ## 0.1.0 (2021-01-05) 348 | 349 | :tada: Initial Release 350 | 351 | - feat: Cloud Functions for Firebase target 352 | - feat: Cloud Run target 353 | - feat: Integrates with existing `functions` 354 | - feat: Supports Firebase configurations with multiple sites 355 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at jthegedus@hey.com. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contribution Guidelines 2 | 3 | Please note that this project is released with a [Contributor Code of Conduct](CODE_OF_CONDUCT.md). By participating in this project you agree to abide by its terms. 4 | 5 | ## How to contribute 6 | 7 | - Open a [GitHub Issue](https://github.com/jthegedus/svelte-adatper-firebase/issues) for a discussion of your idea before working on it 8 | - Fork this repo, develop your solution and submit a PR 9 | 10 | Tooling: 11 | 12 | - [`asdf`](https://asdf-vm.com/) is used to manage the dev environment and system-level tools 13 | - if you do not use `asdf`, then please see `.tool-versions` file for the specific versions of tools. 14 | - `nodejs` dependencies then define the dev tools for this specific package 15 | - prefer [`pnpm`](https://pnpm.js.org/motivation) over `npm` 16 | 17 | Setup: 18 | 19 | ``` 20 | git clone https://github.com/jthegedus/svelte-adapter-firebase.git 21 | asdf install 22 | pnpm i 23 | ``` 24 | 25 | ## What to contribute 26 | 27 | See the [GitHub Issues](https://github.com/jthegedus/svelte-adatper-firebase/issues) list for any open Issue, especially those marked as `help wanted` 28 | 29 | General improvements to any aspect of this adapter are welcome, just ensure major work is preceeded by a conversation in a [GitHub Issue](https://github.com/jthegedus/svelte-adatper-firebase/issues). 30 | 31 | ## Tests 32 | 33 | As an integration point between [SvelteKit](https://kit.svelte.dev) and Firebase Hosting with Function rewrites the tests for this package are **important**. 34 | 35 | The test suite is broken into three categories: 36 | 37 | - **unit**: test internal functions to the CLI & entrypoint JS code 38 | - **integration**: runs the `build` command of SvelteKit with demo apps that tests each path of the src/index.js CLI entrypoint. 39 | - **end-to-end**: runs a shell script which: 40 | - creates the SvelteKit Todo skeleton app (via `npm init@svelte `) 41 | - adds Firebase configuration for Hosting & Cloud Functions 42 | - adds `svelte-adapter-firebase` (relative add of the repo root, not from `npmjs.com`, to test current code changes before publishing) 43 | - creates the Cloud Function which hosts the compiled SvelteKit app (this is the code in `functions/index.js` that the CLI would prompt the user to add) 44 | - installs all dependencies for the Todo app & Cloud Functions 45 | - builds the app 46 | - starts the Firebase Emulator with Hosting & Functions 47 | - makes `curl` requests to the Todo app 48 | - GET to `/` 49 | - GET to `/about` 50 | - GET to `/todos` 51 | - POST with formdata to `/todos` 52 | 53 | - Unit tests are run pre-push. They can be run manually with `pnpm run test` 54 | - Integration tests are run in CI. 55 | - End-to-end tests are run in CI. This script can be run locally with `./tests/end-to-end/test.bash` 56 | 57 | All test suites are run in CI pipelines on PR creation. 58 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020 [these people](https://github.com/jthegedus/svelte-adapter-firebase/graphs/contributors) 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | | :warning: WARNING: Firebase have introduced first-party support for [Web Frameworks](https://firebase.google.com/docs/hosting/frameworks/frameworks-overview). [SvelteKit support is experimental](https://github.com/FirebaseExtended/firebase-framework-tools). This adapter may be deprecated in future as it cannot support the same level of integration as the official team and tooling. Use at your own risk. | 4 | | ---------------------------------------------------------------------------------------------------- | 5 | 6 | ![SvelteKit adapter Firebase social preview](assets/github-preview-svelte-adapter-firebase.png) 7 | 8 | # svelte-adapter-firebase 9 | 10 | [![GitHub Release](https://img.shields.io/github/release/jthegedus/svelte-adapter-firebase.svg?color=green)](https://github.com/jthegedus/svelte-adapter-firebase/releases) 11 | [![npm](https://img.shields.io/npm/v/svelte-adapter-firebase?color=green)](https://www.npmjs.com/package/svelte-adapter-firebase) 12 | [![Tests](https://github.com/jthegedus/svelte-adapter-firebase/actions/workflows/test.yaml/badge.svg)](https://github.com/jthegedus/svelte-adapter-firebase/actions/workflows/test.yaml) 13 | [![CodeQL](https://github.com/jthegedus/svelte-adapter-firebase/actions/workflows/codeql-analysis.yaml/badge.svg)](https://github.com/jthegedus/svelte-adapter-firebase/actions/workflows/codeql-analysis.yaml) 14 | 15 | [Firebase](https://firebase.google.com/) adapter for 16 | [SvelteKit](https://github.com/sveltejs/kit). 17 | 18 | Utilise the Firebase Hosting CDN with dynamic content served by SvelteKit on 19 | Cloud Functions! 20 | 21 | :heavy_check_mark: SSR on 22 | [Cloud Functions](https://firebase.google.com/docs/hosting/functions)
23 | :heavy_check_mark: Integrates with existing Cloud Functions!
24 | :heavy_check_mark: Local production testing with 25 | [Firebase Emulator](https://firebase.google.com/docs/emulator-suite)
26 | :heavy_check_mark: Mitigate cold-starts with 27 | [minInstances](https://firebase.google.com/docs/functions/manage-functions#min-max-instances)
28 | :heavy_check_mark: 29 | [Multiple Hosting Sites](https://firebase.google.com/docs/hosting/multisites#add_additional_sites)
30 | 31 |
32 | 33 | ## Contents 34 | 35 | - [Contents](#contents) 36 | - [Setup](#setup) 37 | - [Beta Adapter Version Compatibility](#beta-adapter-version-compatibility) 38 | - [Configuration Overview](#configuration-overview) 39 | - [Details](#details) 40 | - [`firebase.json` Configurations](#firebasejson-configurations) 41 | - [Adapter Configurations](#adapter-configurations) 42 | - [How it works](#how-it-works) 43 | - [Firebase Emulator local Testing](#firebase-emulator-local-testing) 44 | - [Deployment](#deployment) 45 | - [Caveats](#caveats) 46 | - [Non-goals](#non-goals) 47 | - [FAQ](#faq) 48 | - [Contributing](#contributing) 49 | - [external contributions](#external-contributions) 50 | 51 | ## Setup 52 | 53 | The adapter reads `firebase.json` to determine output dirs for Server scripts & 54 | Static assets, without this file the adapter cannot know how your Firebase app 55 | is configured. Hosting & Cloud Functions are required. 56 | 57 | In your standard SvelteKit project: 58 | 59 | - `npm install --save-dev svelte-adapter-firebase` 60 | - add adapter to `svelte.config.js`: 61 | 62 | ```diff 63 | +import firebase from "svelte-adapter-firebase"; 64 | 65 | /** @type {import('@sveltejs/kit').Config} */ 66 | export default { 67 | kit: { 68 | + adapter: firebase(), 69 | }, 70 | }; 71 | ``` 72 | 73 | - Setup `firebase.json` with `firebase init`. 74 | - `npm run build`. **Read and repeat, the output is meant as a guide. IE: after 75 | your first build, you need to manually add the produced Cloud Function in 76 | `index.js`** 77 | 78 | 79 | 80 | ### Beta Adapter Version Compatibility 81 | 82 | This adapter has not been tested beyond the versions listed below, use at your own risk: 83 | 84 | | Adapter Version | SvelteKit Version | 85 | | --------------- | -------------------- | 86 | | `0.15.0` | `1.1.1` | 87 | | `0.14.5` | `1.0.0-next.587` | 88 | | `0.14.4` | `1.0.0-next.443` | 89 | | `0.14.3` | `1.0.0-next.443` | 90 | | `0.14.2` | `1.0.0-next.405` | 91 | | `0.14.0` | `1.0.0-next.330` | 92 | | `0.13.1` | `1.0.0-next.180` | 93 | | `0.13.0` | `1.0.0-next.168` | 94 | | `0.12.x` | `1.0.0-next.165` | 95 | | `0.11.x` | `1.0.0-next.155` | 96 | | `NA` | `1.0.0-next.152-154` | 97 | | `0.10.x` | `1.0.0-next.132` | 98 | | `0.9.1` | `1.0.0-next.122` | 99 | | `0.9.0` | `1.0.0-next.120` | 100 | | `0.8.x` | `1.0.0-next.111` | 101 | | `NA` | `1.0.0-next.109-110` | 102 | | `0.7.x` | `1.0.0-next.107` | 103 | | `0.6.x` | `1.0.0-next.103` | 104 | | `0.5.x` | `1.0.0-next.54` | 105 | | `0.4.x` | `1.0.0-next.46` | 106 | | `0.3.x` | `1.0.0-next.27` | 107 | 108 | **Note**: only the versions listed have been tested together, if others happen 109 | to work, it is just coincidence. This is beta software after all. 110 | 111 | 112 | 113 | ## Configuration Overview 114 | 115 | Adapter options: 116 | 117 | - `esbuildBuildOptions` 118 | - function to return an `esbuild.BuildOptions` object 119 | - default: see `defaultOptions` object in [`src/index.js`](./src/index.js) 120 | - `firebaseJsonPath` 121 | - path to your `firebase.json` file, **relative** from where `svelte build` is 122 | called 123 | - default: `./firebase.json` 124 | - `target` 125 | - required when `firebase.json:hosting` is an array (contains many site 126 | configurations) 127 | - default: `undefined` 128 | - `sourceRewriteMatch` 129 | - used to lookup the rewrite config to determine whether to output SSR code 130 | for Cloud Functions. See 131 | [Firebase Rewrite configuration docs](https://firebase.google.com/docs/hosting/full-config#rewrite-functions). 132 | - default: `**` 133 | 134 | Adapter output: 135 | 136 | - static assets (images, CSS, Client-side JavaScript) of your SvelteKit app 137 | output to the directory defined by `firebase.json:hosting.public` 138 | - server assets (SSR JavaScript) output alongside your Cloud Functions defined 139 | by `firebase.json:functions.source` 140 | 141 | ## Details 142 | 143 | [Setup](#setup) outlines the steps most commonly used with a single SvelteKit 144 | app. Here we go into the details of each configuration and how it interacts with 145 | the `firebase.json` config. 146 | 147 | The 3 step process is: 148 | 149 | 1. select Hosting config from `firebase.json`. If more than one site present in 150 | config, match `svelte.config.js:target` field with either 151 | `firebase.json:hosting[].site` or `.target` fields. 152 | 2. output static assets to the directory in the `public` field 153 | 3. identify the rewrite rule for SSR to determine Cloud Function output. The 154 | rewrite rule is determined by a lookup of the `rewrites.source` against 155 | `sourceRewriteMatch` 156 | 157 | ### `firebase.json` Configurations 158 | 159 | Due to the relaxed rules of `firebase.json` we can have many valid configs. At a 160 | minimum, one or more Hosting sites is required with an associated Functions 161 | config if a Cloud Function rewrite is used. These are the combintations: 162 | 163 |
164 | single Hosting site with Cloud Function rewrite 165 | 166 | ```json 167 | { 168 | "hosting": { 169 | "public": "", 170 | "rewrites": [ 171 | { 172 | "source": "**", 173 | "function": "" 174 | } 175 | ] 176 | }, 177 | "functions": { 178 | "source": "" 179 | } 180 | } 181 | ``` 182 | 183 |
184 | 185 |
186 | multiple Hosting site with Cloud Function rewrite 187 | 188 | ```json 189 | { 190 | "hosting": [ 191 | { 192 | "site": "blog", 193 | "public": "", 194 | "rewrites": [ 195 | { 196 | "source": "**", 197 | "function": "" 198 | } 199 | ] 200 | }, 201 | { 202 | // another site config 203 | } 204 | ], 205 | "functions": { 206 | "source": "" 207 | } 208 | } 209 | ``` 210 | 211 | To correctly lookup the `blog` site, `target` will need to be set in 212 | `svelte.config.js`: 213 | 214 | ```js 215 | import firebase from "svelte-adapter-firebase"; 216 | 217 | /** @type {import('@sveltejs/kit').Config} */ 218 | export default { 219 | kit: { 220 | adapter: firebase({ target: "blog" }), 221 | }, 222 | }; 223 | ``` 224 | 225 |
226 | 227 | ### Adapter Configurations 228 | 229 | Detailed examples of the adapter configuration options. 230 | 231 | All options: 232 | 233 | ```js 234 | import firebase from "svelte-adapter-firebase"; 235 | 236 | /** @type {import('@sveltejs/kit').Config} */ 237 | export default { 238 | kit: { 239 | adapter: firebase({ 240 | esbuildBuildOptions: (defaultOptions: BuildOptions) => Promise | BuildOptions, 241 | firebaseJsonPath: "", 242 | target: "svelte-func-single-site", 243 | sourceRewriteMatch: "", 244 | }), 245 | }, 246 | }; 247 | ``` 248 | 249 |
250 | esbuildBuildOptions 251 | 252 | As an escape hatch, you may optionally specify a function which will receive the 253 | final esbuild options generated by this adapter and returns a modified esbuild 254 | configuration. The result of this function will be passed as-is to esbuild. The 255 | function can be async. 256 | 257 | For example, you may wish to add `plugins`, or configure the 258 | [`format`](https://esbuild.github.io/api/#format) to bundle to ESM (defaults to 259 | CJS): 260 | 261 | ```js 262 | import firebase from "svelte-adapter-firebase"; 263 | 264 | /** @type {import('@sveltejs/kit').Config} */ 265 | export default { 266 | kit: { 267 | adapter: firebase({ 268 | target: "svelte-func-single-site", 269 | esbuildBuildOptions(defaultOptions) { 270 | return { 271 | ...defaultOptions, 272 | target: "esm" 273 | plugins: [], 274 | }; 275 | }, 276 | }) 277 | }, 278 | }; 279 | ``` 280 | 281 | The default options for this version are as follows: 282 | 283 | ```js 284 | { 285 | entryPoints: ['.svelte-kit/firebase/handler.js'], 286 | outfile: `pathToOutputDir/index.js`, 287 | bundle: true, 288 | inject: ['pathTo/shims.js'], 289 | platform: 'node', 290 | target: `node${functionRuntimeVersion}` 291 | } 292 | ``` 293 | 294 | where esbuild `target` is computed from the Node.js runtime version defined for 295 | your Cloud Functions. 296 | 297 |
298 | 299 |
300 | firebaseJsonPath 301 | 302 | If the `firebase.json` file is not in the directory you run `svelte build`, then 303 | you can set a relative path in `svelte.config.js`: 304 | 305 | ``` 306 | .gitignore 307 | firebase.json 308 | app/ <-- svelte build run in this dir 309 | package.json 310 | svelte.config.js 311 | src/ 312 | anotherApp/ 313 | index.html 314 | index.css 315 | functions/ 316 | package.json 317 | index.js 318 | ``` 319 | 320 | ```js 321 | import firebase from "svelte-adapter-firebase"; 322 | 323 | /** @type {import('@sveltejs/kit').Config} */ 324 | export default { 325 | kit: { 326 | adapter: firebase({ 327 | target: "svelte-func-single-site", 328 | firebaseJsonPath: "../firebase.json", 329 | }), 330 | }, 331 | }; 332 | ``` 333 | 334 |
335 | 336 |
337 | target 338 | 339 | If `firebase.json:hosting` is an array of sites, then each hosting config must 340 | list a `site` or `target` field that matches the adatper's `target` option. For 341 | example: 342 | 343 | ```json 344 | // firebase.json 345 | { 346 | "hosting": [ 347 | { 348 | "site": "blog", 349 | // or 350 | // "target": "blog", 351 | "public": "", 352 | "rewrites": [ 353 | { 354 | "source": "**", 355 | "run": { 356 | "serviceId": "" 357 | } 358 | } 359 | ] 360 | }, 361 | { 362 | "site": "adminPanel", 363 | // or 364 | // "target": "adminPanel", 365 | "public": "" 366 | } 367 | ] 368 | } 369 | ``` 370 | 371 | ```js 372 | import firebase from "svelte-adapter-firebase"; 373 | 374 | /** @type {import('@sveltejs/kit').Config} */ 375 | export default { 376 | kit: { 377 | adapter: firebase({ target: "blog" }), 378 | target: "#svelte", 379 | }, 380 | }; 381 | ``` 382 | 383 | The Firebase config & adapter config match (`firebase.json:hosting[0].site` === 384 | adapter `target`), so therefore we know which Firebase Hosting site you want to 385 | build the SvelteKit site for. 386 | 387 |
388 | 389 |
390 | sourceRewriteMatch 391 | 392 | If the rewrite `source` pattern is not `**`, then `svelte.config.js` 393 | `sourceRewriteMatch` will need to be set to match your desired rewrite rule. For 394 | example: 395 | 396 | ```json 397 | // firebase.json 398 | { 399 | "hosting": { 400 | "public": "", 401 | "rewrites": [ 402 | { 403 | "source": "/blog/**", 404 | "run": { 405 | "serviceId": "" 406 | } 407 | } 408 | ] 409 | } 410 | } 411 | ``` 412 | 413 | ```js 414 | import firebase from "svelte-adapter-firebase"; 415 | 416 | /** @type {import('@sveltejs/kit').Config} */ 417 | export default { 418 | kit: { 419 | adapter: firebase({ sourceRewriteMatch: "/blog/**" }), 420 | target: "#svelte", 421 | }, 422 | }; 423 | ``` 424 | 425 |
426 | 427 | ## How it works 428 | 429 | Given 430 | 431 | - the following `firebase.json` configuration 432 | - a standard SvelteKit app structure 433 | - the default `svelte-adapter-firebase` config 434 | 435 | ```json 436 | // firebase.json 437 | { 438 | "hosting": { 439 | "public": "myApp", 440 | "rewrites": [ 441 | { 442 | "source": "**", 443 | "function": "ssrServer" 444 | } 445 | ], 446 | "predeploy": ["npm run build"] 447 | }, 448 | "functions": { 449 | "source": "functions" 450 | } 451 | } 452 | ``` 453 | 454 | the following Server & Static assets dirs are created: 455 | 456 | ``` 457 | firebase.json ("public": "myApp") 458 | package.json 459 | svelte.config.js 460 | src/ 461 | app.html 462 | routes/ 463 | index.svelte 464 | functions/ 465 | package.json ("main": "index.js") 466 | index.js 467 | sveltekit/ <-- Server Assets dir (code to be imported to you Cloud Function) 468 | myApp/ <-- Static Assets to go to Firebase Hosting CDN 469 | ``` 470 | 471 | - `firebase.json:functions.source` dir is used to find `functions/package.json` 472 | whose `main` field is used to find the Cloud Function build dir. This is used 473 | as the server asset output dir. 474 | 475 |
476 | TypeScript Cloud Functions 477 | 478 | Because we use the above method to determine the output dir, the server assets 479 | are output to the correct place when using TypeScript. 480 | 481 | ``` 482 | firebase.json ("public": "myApp") 483 | package.json 484 | svelte.config.js 485 | src/ 486 | app.html 487 | routes/ 488 | index.svelte 489 | functions/ 490 | package.json ("main": "lib/index.js") 491 | index.ts 492 | lib/ 493 | index.js 494 | sveltekit/ <-- Server assets output to functions/lib 495 | myApp/ <-- Static assets to go to Firebase Hosting CDN 496 | ``` 497 | 498 |
499 | 500 |
501 | Output with Multiple Sites 502 | 503 | In a multi-site setup, the `site` or `target` field from hosting config in 504 | `firebase.json` is used as the server output dir: 505 | 506 | ``` 507 | firebase.json ("site": "myCoolSite","public": "myApp") 508 | package.json 509 | svelte.config.js 510 | src/ 511 | app.html 512 | routes/ 513 | index.svelte 514 | functions/ 515 | package.json 516 | index.js 517 | myCoolSite/ <-- Server assets 518 | myApp/ <-- Static assets to go to Firebase Hosting CDN 519 | ``` 520 | 521 |
522 | 523 | The final piece is to write the actual Cloud Function source code to reference 524 | the output server assets. The code is printed during `svelte build` and should 525 | be placed in your `index.js` or `index.ts` manually. 526 | 527 | This is a flexible solution that allows integrating with other Cloud Functions 528 | in your project. You can edit the provided code as you see fit. The 529 | import/require of the generated code will not change unless you change the 530 | `firebase.json:hosting.site` or `package.json:main` fields, so you shouldn't 531 | need to update this code after adding it. 532 | 533 | ## Firebase Emulator local Testing 534 | 535 | Test your production build locally before pushing to git or deploying! 536 | 537 | - build your app: `svelte-kit build` 538 | - install Function dependencies: `pnpm install --prefix functions` 539 | - start the emulator: `firebase emulators:start` 540 | 541 | ## Deployment 542 | 543 | `firebase deploy` :tada: 544 | 545 | ## Caveats 546 | 547 | - Using `firebase.json:hosting[].site` is preferred to 548 | `firebase.json:hosting[].target` as 549 | [Firebase Deploy Targets](https://firebase.google.com/docs/cli/targets) only 550 | supports Hosting, Storage & Databases and not Functions. This means you can 551 | use Deploy targets `target` field to identify your site for the adapter to 552 | **build**, but you **CANNOT use Deploy Targes when deploying** as you need to 553 | deploy the Hosting _& Functions_ at the same time for this solution to work as 554 | expected. 555 | - [Firebase Hosting Preview Channels](https://firebase.google.com/docs/hosting/test-preview-deploy) 556 | currently lacks first-party support for SSR applications. This adapter doesn't 557 | attempt to remedy this issue and doesn't produce a different SSR Function for 558 | preview channel deployments. 559 | - :warning: Cloud Function rewrites only support **us-central1**, other regions 560 | will error. The official warning about this can be found in 561 | [these docs](https://firebase.google.com/docs/hosting/functions). 562 | 563 | ## Non-goals 564 | 565 | > Write Cloud Function code directly into `.js` file instead of printing in 566 | > console. 567 | 568 | Firebase Cloud Functions have a long history of people configuring their index 569 | files completely differently, some even generating them from directories. 570 | Accommodating these would be a headache. Instead, all we look for is a match 571 | against this string, `${name} =`, where `name` is your Cloud Functions name. We 572 | may make this configurable to a specific file in future. 573 | 574 | Additionally, this allows for users to customise their Firebase Cloud Function 575 | API like `runWith()` options for memory/CPU, min/max Instances and 576 | VPC/Ingress/Egress configuration settings, without complex support for options 577 | in the adapter. This keeps the Function config where it should, close to the 578 | userland code. 579 | 580 | > Handle the deployment of the app to Firebase. 581 | 582 | Firebase apps consist of many different services with the CLI providing optional 583 | deployments. We do not want to dictate full deployments with your frontend nor 584 | perform partial deployments if it does not fit your app. The only option then is 585 | to leave it to you :tada: 586 | 587 | ## FAQ 588 | 589 | > Why is the Cloud Function code output to the terminal for me to add manually 590 | > instead of being written to `functions/index.js`? 591 | 592 | See [non-goals](#non-goals) _Write Cloud Function code directly into `.js` file 593 | instead of printing in console._ 594 | 595 | > Firebase libs in SvelteKit 596 | 597 | As recommended in the [SvelteKit FAQ](https://kit.svelte.dev/faq), please use 598 | [Firebase JS SDK v9](https://firebase.google.com/docs/web/learn-more#modular-version) 599 | as the older version of the SDK has issues and a larger bundle size. 600 | 601 | > Cold Starts 602 | 603 | Since the purpose of using this adapter is to leverage the Firebase Hosting CDN, 604 | you should consider improving the user experience with targetted caching/TTLs. 605 | 606 | If cold start are still an issue for your application, Cloud Functions has 607 | support for `minInstances` which will keep `X` number of instances warm. From 608 | the docs: 609 | 610 | > A minimum number of instances kept running incur billing costs at idle rates. 611 | > Typically, to keep one idle function instance warm costs less than $6.00 a 612 | > month. The Firebase CLI provides a cost estimate at deployment time for 613 | > functions with reserved minimum instances. Refer to Cloud Functions Pricing to 614 | > calculate costs. 615 | > 616 | > -[Firebase docs](https://firebase.google.com/docs/functions/manage-functions#min-max-instances) 617 | 618 | To implement this, configure your 619 | [`runWith`](https://github.com/firebase/firebase-functions/blob/d46ec6191e61f560f3f21f13333e0f3285d3de90/src/function-configuration.ts#L101) 620 | options like so: 621 | 622 | ```diff 623 | const myRuntimeOptions = { 624 | memory: "1GB", 625 | + minInstances: 1, 626 | } 627 | exports.myFunc = functions.runWith(myRuntimeOptions).https.onRequest(async (request, response) => { 628 | ... 629 | }); 630 | ``` 631 | 632 | Note: this is still single concurrency (if an instance does not exist to handle 633 | a request when it hits the backend a new Function instance is created). Watch 634 | this space! 635 | 636 | 637 | 638 | - `1.0.0` will not be published until the SvelteKit Adapter API is declared 639 | stable and SvelteKit is released for general use. 640 | 641 | 642 | 643 | ## Contributing 644 | 645 | [Contributions of any kind welcome, just follow the 646 | guidelines](CONTRIBUTING.md)! 647 | 648 | Short version: 649 | 650 | ``` 651 | git clone https://github.com/jthegedus/svelte-adapter-firebase.git 652 | asdf install 653 | pnpm i 654 | ``` 655 | 656 | See [asdf](https://asdf-vm.com) to install set it up. 657 | 658 | ### external contributions 659 | 660 | While building this adapter some issues were found with upstream components, 661 | these are captured here should someone wish to contribute them: 662 | 663 | - Cloud Function validation code linked in `utils.js` is from two different 664 | sources which indicates that it is being validated by `firebase-tools` in two 665 | separate places. PR a fix there. 666 | -------------------------------------------------------------------------------- /assets/github-preview-svelte-adapter-firebase.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jthegedus/svelte-adapter-firebase/c7a56cb4187b6f4f90aecb212e0b731e78c009ce/assets/github-preview-svelte-adapter-firebase.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "svelte-adapter-firebase", 3 | "description": "Adapter for Svelte apps that integrates a Firebase Hosting Website with Cloud Functions or Cloud Run for server side rendering.", 4 | "repository": { 5 | "type": "git", 6 | "url": "https://github.com/jthegedus/svelte-adapter-firebase.git" 7 | }, 8 | "homepage": "https://github.com/jthegedus/svelte-adapter-firebase#readme", 9 | "bugs": { 10 | "url": "https://github.com/jthegedus/svelte-adapter-firebase/issues" 11 | }, 12 | "author": "James Hegedus ", 13 | "license": "MIT", 14 | "keywords": [ 15 | "svelte", 16 | "sveltekit", 17 | "sveltekit-adapter", 18 | "firebase", 19 | "cloud-functions", 20 | "cloud-run", 21 | "serverless", 22 | "ssr" 23 | ], 24 | "version": "0.15.0", 25 | "type": "module", 26 | "exports": { 27 | ".": { 28 | "import": "./src/index.js" 29 | }, 30 | "./package.json": "./package.json" 31 | }, 32 | "main": "src/index.js", 33 | "types": "src/index.d.ts", 34 | "files": [ 35 | "src" 36 | ], 37 | "engines": { 38 | "node": "^14.13.1 || >= 16" 39 | }, 40 | "dependencies": { 41 | "esbuild": "^0.17.0" 42 | }, 43 | "peerDependencies": { 44 | "@sveltejs/kit": "^1.0.0-next.587" 45 | }, 46 | "devDependencies": { 47 | "@sveltejs/kit": "^1.1.1", 48 | "@types/express": "^4.17.13", 49 | "@types/node": "^18.7.3", 50 | "ava": "^5.1.1", 51 | "firebase-functions": "^4.1.1", 52 | "xo": "^0.53.1" 53 | }, 54 | "scripts": { 55 | "fix": "xo --fix", 56 | "test": "xo && ava" 57 | }, 58 | "ava": { 59 | "files": [ 60 | "tests/unit/**/*" 61 | ] 62 | }, 63 | "xo": { 64 | "ignores": [ 65 | "tests/end-to-end", 66 | "tests/integration" 67 | ], 68 | "rules": { 69 | "unicorn/prefer-node-protocol": "off", 70 | "n/file-extension-in-import": "off" 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/files/entry.js: -------------------------------------------------------------------------------- 1 | import process from 'process'; 2 | import {Server} from 'SERVER'; 3 | import {manifest} from 'MANIFEST'; 4 | import {toSvelteKitRequest} from './firebase-to-svelte-kit.js'; 5 | 6 | const server = new Server(manifest); 7 | 8 | /** 9 | * Firebase Cloud Function handler for SvelteKit 10 | * 11 | * This function converts the Firebase Cloud Function (Express.js) Request object 12 | * into a format consumable to the SvelteKit render() function 13 | * 14 | * Relevant documentation - https://firebase.google.com/docs/functions/http-events#read_values_from_the_request 15 | * 16 | * @param {import('firebase-functions').https.Request} request 17 | * @param {import('express').Response} response 18 | * @returns {Promise} 19 | */ 20 | export default async function svelteKit(request, response) { 21 | await server.init({ 22 | env: process.env, 23 | }); 24 | 25 | const rendered = await server.respond(toSvelteKitRequest(request), { 26 | getClientAddress() { 27 | return request.headers.get('x-forwarded-for'); 28 | }, 29 | }); 30 | const body = await rendered.arrayBuffer(); 31 | 32 | return rendered 33 | ? response.writeHead(rendered.status, Object.fromEntries(rendered.headers)).end(Buffer.from(body)) 34 | : response.writeHead(404, 'Not Found').end(); 35 | } 36 | -------------------------------------------------------------------------------- /src/files/firebase-to-svelte-kit.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Convert Firebase Cloud Function Request to SvelteKit Request 3 | * 4 | * @param {import('firebase-functions').https.Request} request 5 | * @return {import('@sveltejs/kit').IncomingRequest} 6 | */ 7 | export function toSvelteKitRequest(request) { 8 | // Firebase sometimes omits the protocol used. Default to http. 9 | const protocol = request.headers['x-forwarded-proto'] || 'http'; 10 | // Firebase forwards the request to sveltekit, use the forwarded host. 11 | const host = `${protocol}://${request.headers['x-forwarded-host']}`; 12 | const {href, pathname, searchParams: searchParameters} = new URL(request.url || '', host); 13 | // eslint-disable-next-line no-undef 14 | return new Request(href, { 15 | method: request.method, 16 | headers: toSvelteKitHeaders(request.headers), 17 | body: request.rawBody ?? null, 18 | host, 19 | path: pathname, 20 | query: searchParameters, 21 | }); 22 | } 23 | 24 | /** 25 | * Convert Node.js http.IncomingHttpHeaders to SvelteKit Record 26 | * 27 | * This is achieved by converting the all string[] header values to the expected type of a CSV string. 28 | * 29 | * Example: 30 | * input = { 'Content-Type': 'application/json', 'set-cookie': ['something', 'another'] } 31 | * output = { 'Content-Type': 'application/json', 'set-cookie': 'something,another' } 32 | * 33 | * @param {import('http').IncomingHttpHeaders} headers 34 | * @returns {Record} 35 | */ 36 | export function toSvelteKitHeaders(headers) { 37 | /** @type {Record} */ 38 | const finalHeaders = {}; 39 | 40 | // Assume string | string[] types for all values 41 | for (const [key, value] of Object.entries(headers)) { 42 | finalHeaders[key] = Array.isArray(value) 43 | ? value.join(',') 44 | : value; 45 | } 46 | 47 | return finalHeaders; 48 | } 49 | -------------------------------------------------------------------------------- /src/files/shims.js: -------------------------------------------------------------------------------- 1 | import {installPolyfills} from '@sveltejs/kit/node/polyfills'; 2 | 3 | installPolyfills(); 4 | -------------------------------------------------------------------------------- /src/index.d.ts: -------------------------------------------------------------------------------- 1 | import type {Adapter} from '@sveltejs/kit'; 2 | import type {BuildOptions} from 'esbuild'; 3 | 4 | type Options = { 5 | esbuildOptions?: (defaultOptions: BuildOptions) => Promise; 6 | firebaseJsonPath?: string; 7 | target?: string; 8 | sourceRewriteMatch?: string; 9 | }; 10 | 11 | export default function plugin(options?: Options): Adapter; 12 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import {readFileSync, writeFileSync} from 'fs'; 2 | import path from 'path'; 3 | import process from 'process'; 4 | import {fileURLToPath} from 'url'; 5 | import esbuild from 'esbuild'; 6 | import { 7 | ensureStaticResourceDirsDiffer, 8 | logRelativeDir, 9 | parseFirebaseConfiguration, 10 | } from './utils.js'; 11 | 12 | const adapterName = 'svelte-adapter-firebase'; 13 | 14 | /** @type {import('.')} **/ 15 | const entrypoint = function (options = {}) { 16 | return { 17 | name: adapterName, 18 | async adapt(builder) { 19 | const { 20 | esbuildOptions = undefined, 21 | firebaseJsonPath = 'firebase.json', 22 | target = undefined, 23 | sourceRewriteMatch = '**', 24 | } = options; 25 | 26 | builder.log.minor(`Adapter configuration:\n\t${JSON.stringify(options)}`); 27 | const {functions, publicDir} = parseFirebaseConfiguration({firebaseJsonPath, target, sourceRewriteMatch}); 28 | ensureStaticResourceDirsDiffer({source: path.join(process.cwd(), builder.config.kit.files.assets), dest: publicDir}); 29 | const functionsPackageJson = JSON.parse(readFileSync(path.join(functions.source, 'package.json'), 'utf8')); 30 | if (!functionsPackageJson?.main) { 31 | throw new Error(`Error reading ${functionsPackageJson}. Required field "main" missing.`); 32 | } 33 | 34 | const dirs = { 35 | files: fileURLToPath(new URL('files', import.meta.url)), 36 | serverDirname: functions.name ?? 'svelteKit', 37 | serverPath: path.join(functions.source, path.dirname(functionsPackageJson.main), functions.name ?? 'svelteKit'), 38 | tmp: builder.getBuildDirectory(adapterName), 39 | }; 40 | const ssrFunc = { 41 | entrypoint: path.join(functions.source, functionsPackageJson.main), 42 | svelteSSR: dirs.serverDirname.replace(/\W/g, '') + 'Server', 43 | }; 44 | 45 | const relativePath = path.posix.relative(dirs.tmp, builder.getServerDirectory()); 46 | const runtimeVersion = functions.runtime || functionsPackageJson?.engines?.node || '18'; 47 | builder.rimraf(dirs.tmp); 48 | builder.rimraf(dirs.serverPath); 49 | builder.copy( 50 | path.join(dirs.files, 'entry.js') 51 | , path.join(dirs.tmp, 'entry.js'), { 52 | replace: {SERVER: `${relativePath}/index.js`, MANIFEST: `${relativePath}/manifest.js`}, 53 | }); 54 | builder.copy(path.join(dirs.files, 'firebase-to-svelte-kit.js'), path.join(dirs.tmp, 'firebase-to-svelte-kit.js')); 55 | 56 | /** @type {esbuild.BuildOptions} */ 57 | const defaultOptions = { 58 | entryPoints: [path.join(dirs.tmp, 'entry.js')], 59 | outfile: path.join(dirs.serverPath, 'index.js'), 60 | bundle: true, 61 | inject: [path.join(dirs.files, 'shims.js')], 62 | platform: 'node', 63 | target: `node${runtimeVersion}`, 64 | }; 65 | 66 | const buildOptions = esbuildOptions 67 | ? await esbuildOptions(defaultOptions) 68 | : defaultOptions; 69 | await esbuild.build(buildOptions); 70 | builder.log.minor(logRelativeDir('Writing Cloud Function server assets to', dirs.serverPath)); 71 | 72 | try { 73 | if (!readFileSync(ssrFunc.entrypoint, 'utf8').includes(`${functions.name} =`)) { 74 | builder.log.warn(`Add the following Cloud Function to ${ssrFunc.entrypoint}`); 75 | builder.log.warn(` 76 | let ${ssrFunc.svelteSSR}; 77 | exports.${functions.name} = functions.region("us-central1").https.onRequest(async (request, response) => { 78 | if (!${ssrFunc.svelteSSR}) { 79 | functions.logger.info("Initialising SvelteKit SSR entry"); 80 | ${ssrFunc.svelteSSR} = require("./${dirs.serverDirname}/index").default; 81 | functions.logger.info("SvelteKit SSR entry initialised!"); 82 | } 83 | functions.logger.info("Requested resource: " + request.originalUrl); 84 | return ${ssrFunc.svelteSSR}(request, response); 85 | }); 86 | `); 87 | } 88 | } catch (error) { 89 | throw new Error(`Error reading Cloud Function entrypoint file: ${ssrFunc.entrypoint}. ${error.message}`); 90 | } 91 | 92 | builder.log.minor(logRelativeDir('Erasing destination static asset dir before processing', publicDir)); 93 | builder.rimraf(publicDir); 94 | 95 | builder.log.minor(logRelativeDir('Writing client application to', publicDir)); 96 | builder.writeClient(publicDir); 97 | 98 | builder.log.minor(logRelativeDir('Prerendering static pages to', publicDir)); 99 | builder.writePrerendered(publicDir); 100 | writeFileSync(`${dirs.tmp}/manifest.js`, `export const manifest = ${builder.generateManifest({ 101 | relativePath, 102 | })};\n`); 103 | builder.log.minor('Writing routes...'); 104 | 105 | builder.mkdirp(`${dirs.tmp}/config`); 106 | writeFileSync( 107 | `${dirs.tmp}/config/routes.json`, 108 | JSON.stringify([ 109 | { 110 | src: `/${builder.config.kit.appDir}/.+`, 111 | headers: { 112 | 'cache-control': 'public, immutable, max-age=31536000', 113 | }, 114 | }, 115 | { 116 | handle: 'filesystem', 117 | }, 118 | ]), 119 | ); 120 | }, 121 | }; 122 | }; 123 | 124 | export default entrypoint; 125 | -------------------------------------------------------------------------------- /src/utils.js: -------------------------------------------------------------------------------- 1 | import {existsSync, readFileSync} from 'fs'; 2 | import path from 'path'; 3 | import process from 'process'; 4 | 5 | /** 6 | * @typedef CloudRunRewriteConfig 7 | * @type {object} cloudrun 8 | * @property {undefined|'us-west1'} cloudrun.region 9 | * @property {undefined|string} cloudrun.serviceId 10 | */ 11 | 12 | /** 13 | * @typedef HostingRewriteConfig 14 | * @type {object} rewrite 15 | * @property {undefined|string} rewrite.function 16 | * @property {undefined|CloudRunRewriteConfig} rewrite.run 17 | * @property {undefined|string} rewrite.source 18 | */ 19 | 20 | /** 21 | * @typedef HostingConfig 22 | * @type {object} hosting 23 | * @property {undefined|string} hosting.public 24 | * @property {undefined|Array.} hosting.rewrites 25 | * @property {undefined|string} hosting.site 26 | * @property {undefined|string} hosting.target 27 | */ 28 | 29 | /** 30 | * @typedef FunctionsConfig 31 | * @type {object} functions 32 | * @property {undefined|'nodejs14'|'nodejs16'} functions.runtime 33 | * @property {undefined|string} functions.source 34 | */ 35 | 36 | /** 37 | * Firebase configuration from `firebase.json`. Typed with the types required by the adapter. 38 | * 39 | * @typedef FirebaseConfig 40 | * @type {object} config 41 | * @property {undefined|FunctionsConfig} functions 42 | * @property {undefined|HostingConfig|Array.} hosting 43 | */ 44 | 45 | /** 46 | * 47 | * @param {any} parameter 48 | * @returns {boolean} true if param is a string 49 | */ 50 | function isString(parameter) { 51 | return (typeof parameter === 'string' || parameter instanceof String); 52 | } 53 | 54 | /** 55 | * Extract Hosting Config from FirebaseConfig 56 | * @type {FirebaseConfig} 57 | * sourceRewriteMatch: string; 58 | * target: string|undefined; 59 | */ 60 | function extractHostingConfig(firebaseConfig, sourceRewriteMatch, target) { 61 | // Force "hosting" field to be array 62 | const firebaseHostingConfig = Array.isArray(firebaseConfig.hosting) 63 | ? firebaseConfig.hosting 64 | : [{...firebaseConfig.hosting}]; 65 | 66 | // Force "site" field to be included in "hosting" if more than 1 hosting site config 67 | if (firebaseHostingConfig.length > 1) { 68 | for (const item of firebaseHostingConfig) { 69 | if (!item.site && !item.target) { 70 | throw new Error('Error: Multiple "hosting" configurations found, each requires either a "site" field or "target" field, one does not. https://firebase.google.com/docs/hosting/multisites'); 71 | } 72 | } 73 | } 74 | 75 | const hostingConfig = firebaseHostingConfig.length === 1 76 | ? firebaseHostingConfig[0] 77 | : firebaseHostingConfig.find(item => (item.site === target && item.site !== undefined) || (item.target === target && item.target !== undefined)); 78 | 79 | if (!hostingConfig) { 80 | if (!target) { 81 | throw new Error('Error: Multiple "hosting" configurations found, but no "target" specified in "svelte.config.js" adapter config. Provide one so we can match the config correctly.'); 82 | } 83 | 84 | const hostingConfigValues = firebaseHostingConfig.map(item => (item.target ? {target: item.target} : {site: item.site})); 85 | throw new Error(`Error: Multiple "hosting" configurations found in "firebase.json" but not match found for ${target} specified in "svelte.config.js" adapter config. "hosting[].site" & "hosting[].target" values ${JSON.stringify(hostingConfigValues)}`); 86 | } 87 | 88 | if (!isString(hostingConfig?.public)) { 89 | throw new Error('Error: Required "hosting.public" field not found for hosting configuration.'); 90 | } 91 | 92 | if (isString(hostingConfig?.public) && hostingConfig.public === '') { 93 | throw new Error('Error: Required "hosting.public" field is an empty string, a directory is required.'); 94 | } 95 | 96 | if (!Array.isArray(hostingConfig?.rewrites)) { 97 | throw new TypeError(`Error: Required "hosting[].rewrites" field not found for matched hosting configuration. Specify your Cloud Function with rewrite rule matching "source":"${sourceRewriteMatch}"`); 98 | } 99 | 100 | return hostingConfig; 101 | } 102 | 103 | /** 104 | * Parse provided firebase.json to mat 105 | against SvelteKit config for target & Rewrite rule. 106 | * 107 | * @param {{ 108 | * firebaseJsonPath: string 109 | * sourceRewriteMatch: string; 110 | * target: string|undefined; 111 | * }} param 112 | * @returns {{ 113 | * functions: { name: string, source: string, runtime: string | undefined }; 114 | * publicDir: string 115 | * }} Functions config with `public` dir 116 | */ 117 | function parseFirebaseConfiguration({target, sourceRewriteMatch, firebaseJsonPath}) { 118 | const firebaseJson = path.resolve(firebaseJsonPath); 119 | 120 | if (!existsSync(firebaseJson)) { 121 | throw new Error(`Error: The adapter requires a "firebase.json" file. "firebaseJsonPath:${firebaseJsonPath}" does not exist.`); 122 | } 123 | 124 | /** 125 | * @type {FirebaseConfig} 126 | */ 127 | let firebaseConfig; 128 | try { 129 | firebaseConfig = JSON.parse(readFileSync(firebaseJson, 'utf8')); 130 | } catch (error) { 131 | throw new Error(`Error: failure while parsing ${firebaseJsonPath}. ${error.message}`); 132 | } 133 | 134 | if (!firebaseConfig?.hosting) { 135 | throw new Error('Error: "hosting" config missing from "firebase.json"'); 136 | } 137 | 138 | const hostingConfig = extractHostingConfig(firebaseConfig, sourceRewriteMatch, target); 139 | 140 | const rewriteConfig = hostingConfig.rewrites.find(item => item.source === sourceRewriteMatch && (item.function || item.run)); 141 | 142 | if (!rewriteConfig) { 143 | throw new Error(`Error: Required "hosting[].rewrites[]" does not contain a config with "source":"${sourceRewriteMatch}" and either "function":"" or "run":{...} entries`); 144 | } 145 | 146 | if (rewriteConfig?.run && (!rewriteConfig.run.serviceId || !isString(rewriteConfig.run.serviceId))) { 147 | throw new Error('Error: Required "serviceId" field not found for Cloud Run rewrite rule in "firebase.json"'); 148 | } 149 | 150 | if (rewriteConfig?.run && !validCloudRunServiceId(rewriteConfig.run.serviceId)) { 151 | throw new Error('Error: Cloud Run "serviceId" must use only lowercase alphanumeric characters and dashes cannot begin or end with a dash, and cannot be longer than 63 characters.'); 152 | } 153 | 154 | if (rewriteConfig?.run && rewriteConfig?.run?.region && (rewriteConfig?.run?.region !== 'us-west1')) { 155 | throw new Error('Error: Cloud Run "region" is invalid, it should be "use-west1".'); 156 | } 157 | 158 | if (rewriteConfig?.function && !validCloudFunctionName(rewriteConfig.function)) { 159 | throw new Error('Error: Cloud Function name must use only alphanumeric characters and underscores and cannot be longer than 63 characters'); 160 | } 161 | 162 | // If function, ensure function root-level field is present. If functions is an array, gets the first entry 163 | let functions; 164 | if (firebaseConfig?.functions) { 165 | functions = Array.isArray(firebaseConfig.functions) ? firebaseConfig.functions[0] : firebaseConfig.functions; 166 | if (!functions || !isString(functions.source)) { 167 | throw new Error('Error: Required "functions.source" or "functions[].source" field is missing from Firebase Configuration file.'); 168 | } 169 | } else { 170 | throw new Error('Error: Required "functions" field is missing from Firebase Configuration file.'); 171 | } 172 | 173 | return { 174 | functions: { 175 | name: rewriteConfig.function ?? rewriteConfig.run.serviceId, 176 | source: path.join(path.dirname(firebaseJson), functions.source), 177 | runtime: functions?.runtime, 178 | }, 179 | publicDir: path.join(path.dirname(firebaseJson), hostingConfig.public), 180 | }; 181 | } 182 | 183 | /** 184 | * Cloud Run Service ID rules: 185 | * - only lowercase alphanumeric characters and dashes 186 | * - cannot begin or end with a dash 187 | * - cannot be longer than 63 characters 188 | * @param {string} serviceId 189 | * @returns {boolean} `true` if valid 190 | */ 191 | function validCloudRunServiceId(serviceId) { 192 | return /^[a-z\d][a-z\d-]+[a-z\d]$/gm.test(serviceId) && serviceId.length < 64; 193 | } 194 | 195 | /** 196 | * Cloud Function name rules: 197 | * - alphanumeric 198 | * - underscore 199 | * - max length 63 chars 200 | * @param {string} name 201 | * @returns {boolean} `true` if valid 202 | * 203 | * Rules a combination of 204 | * - https://github.com/firebase/firebase-tools/blob/1633f4fccbbc1bcbc6216fe13b8e888c8940bde4/src/deploy/functions/validate.ts#L38 205 | * - https://github.com/firebase/firebase-tools/blob/2dc7216a498dee2ca7e2acc33d6ba16d5647e27f/src/extractTriggers.js#L18 206 | */ 207 | function validCloudFunctionName(name) { 208 | return /^\w{1,63}$/.test(name); 209 | } 210 | 211 | /** 212 | * Ensure provided static asset output dir (firebase.json:hosting.public) is not the same as the source dir. 213 | * Throw error if invalid. 214 | * 215 | * @param {{ 216 | * dest:string 217 | * source:string 218 | * }} param source and destination directory for static assets 219 | */ 220 | function ensureStaticResourceDirsDiffer({source, dest}) { 221 | if (source === dest) { 222 | throw new Error(`Error: "firebase.json:hosting.public" field (${dest}) must be a different directory to "svelte.config.js:kit.files.assets" field (${source}).`); 223 | } 224 | } 225 | 226 | /** 227 | * Format message with relative dir on following newline. 228 | * 229 | * @param {string} message 230 | * @param {string} dir 231 | * @returns {string} formatted message with relative dir on following newline 232 | */ 233 | function logRelativeDir(message, dir) { 234 | return `${message}:\n\t${path.relative(process.cwd(), dir)}`; 235 | } 236 | 237 | export { 238 | ensureStaticResourceDirsDiffer, 239 | logRelativeDir, 240 | parseFirebaseConfiguration, 241 | validCloudRunServiceId, 242 | validCloudFunctionName, 243 | }; 244 | -------------------------------------------------------------------------------- /tests/end-to-end/scaffold/.firebaserc: -------------------------------------------------------------------------------- 1 | {"projects":{"default":"demo"}} 2 | -------------------------------------------------------------------------------- /tests/end-to-end/scaffold/firebase.json: -------------------------------------------------------------------------------- 1 | { 2 | "hosting": [{ 3 | "public": "public", 4 | "site": "svelte-func-single-site", 5 | "rewrites": [ 6 | { 7 | "source": "/about", 8 | "destination": "/about.html" 9 | }, 10 | { 11 | "source": "**", 12 | "function": "sveltekit" 13 | } 14 | ] 15 | }], 16 | "functions": { 17 | "source": "functions" 18 | }, 19 | "emulators": { 20 | "ui": { 21 | "enabled": false 22 | }, 23 | "hosting": { 24 | "port": 8685 25 | }, 26 | "functions": { 27 | "port": 5001 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /tests/end-to-end/scaffold/functions/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | 3 | # Specific to this app's firebase.json 4 | # ignore SvelteKit build output in Cloud Function 5 | sveltekit/ 6 | -------------------------------------------------------------------------------- /tests/end-to-end/scaffold/functions/index.js: -------------------------------------------------------------------------------- 1 | const functions = require('firebase-functions'); 2 | 3 | let sveltekitServer; 4 | exports.sveltekit = functions.https.onRequest(async (request, response) => { 5 | if (!sveltekitServer) { 6 | functions.logger.info('Initialising SvelteKit SSR Handler'); 7 | sveltekitServer = require('./sveltekit/index').default; 8 | functions.logger.info('SvelteKit SSR Handler initialised!'); 9 | } 10 | 11 | functions.logger.info('Requested resource: ' + request.originalUrl); 12 | return sveltekitServer(request, response); 13 | }); 14 | -------------------------------------------------------------------------------- /tests/end-to-end/scaffold/functions/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "functions", 3 | "description": "Cloud Functions for Firebase", 4 | "scripts": { 5 | "serve": "firebase emulators:start --only functions", 6 | "shell": "firebase functions:shell", 7 | "start": "npm run shell", 8 | "deploy": "firebase deploy --only functions", 9 | "logs": "firebase functions:log" 10 | }, 11 | "engines": { 12 | "node": "18" 13 | }, 14 | "main": "index.js", 15 | "dependencies": { 16 | "firebase-admin": "^11.3.0", 17 | "firebase-functions": "^4.1.1" 18 | }, 19 | "devDependencies": { 20 | "firebase-functions-test": "^3.0.0" 21 | }, 22 | "private": true 23 | } 24 | -------------------------------------------------------------------------------- /tests/end-to-end/scaffold/svelte.config.js: -------------------------------------------------------------------------------- 1 | import firebase from 'svelte-adapter-firebase'; 2 | 3 | /** @type {import('@sveltejs/kit').Config} */ 4 | const config = { 5 | kit: { 6 | // Hydrate the
element in src/app.html 7 | adapter: firebase() 8 | } 9 | }; 10 | 11 | export default config; 12 | -------------------------------------------------------------------------------- /tests/end-to-end/test.bash: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # undefined vars are errors 4 | set -u 5 | IFS=$'\n\t' 6 | 7 | # Execute end-to-end tests of the SvelteKit Todo template app built with 8 | # the svelte-adapter-firebase adapter hosted on the Cloud Function using 9 | # the Firebase Emulator in CI 10 | # 11 | # Curl API and assert response payload 12 | # 13 | # Usage: 14 | # 15 | # tests/end-to-end/test.bash 16 | 17 | SCRIPT_PATH=$(dirname "$(realpath -s "$0")") 18 | TEST_DIR="$(mktemp -dt svelte-adapter-firebase-XXXX)" 19 | PORT="8685" # from test-firebase.json 20 | INDICATOR="====> " 21 | 22 | # Cleanup files on exit 23 | trap 'echo "${INDICATOR}Exiting, removing ${TEST_DIR} & killing all processes matching _firebase_" && rm -rf -- "$TEST_DIR" && pkill -f firebase' EXIT 24 | 25 | echo "TEST_DIR: ${TEST_DIR}" 26 | echo "PWD: ${PWD}" 27 | 28 | echo "${INDICATOR}Install svelte-adapter-firebase ${SCRIPT_PATH}/../../ deps" 29 | npm install 30 | 31 | echo "${INDICATOR}init SvelteKit Sverdle app" 32 | yes "" | "$(npm create svelte@latest "${TEST_DIR}")" 33 | echo "${INDICATOR}Complete SvelteKit create" 34 | 35 | cp -R "${SCRIPT_PATH}"/scaffold/. "${TEST_DIR}" 36 | cp ".tool-versions" "${TEST_DIR}/.tool-versions" 37 | 38 | cd "${TEST_DIR}" || exit 1 39 | echo "${INDICATOR}PWD after cd to TEST_DIR: ${PWD}" 40 | 41 | echo "${INDICATOR}Install kit template deps" 42 | npm install 43 | 44 | echo "${INDICATOR}Install svelte-adapter-firebase from ${SCRIPT_PATH}/../" 45 | npm install "${SCRIPT_PATH}/../../" 46 | 47 | echo "${INDICATOR}Install functions/ deps" 48 | npm --prefix functions install 49 | 50 | echo "${INDICATOR}Build Kit todos site" 51 | npm run build 52 | 53 | echo "${INDICATOR}Install firebase-tools" 54 | npm install firebase-tools 55 | 56 | echo "${INDICATOR}Starting emulator" 57 | npx firebase emulators:start --only functions,hosting & 58 | 59 | sleep 8 60 | 61 | echo "${INDICATOR}Test GET static page '/about'" 62 | EXPECTED_SUBSTRING="The page you're looking at is purely static HTML" 63 | RESULT="$(curl -sL localhost:${PORT}/about)" 64 | 65 | if [[ "${RESULT}" != *"${EXPECTED_SUBSTRING}"* ]]; then 66 | echo "Failed testing localhost:${PORT}/about" 67 | echo -e "Expect --> ${EXPECTED_SUBSTRING}\nGot -->\n${RESULT}" 68 | exit 1 69 | fi 70 | 71 | echo "${INDICATOR}Test GET SSR route '/'" 72 | EXPECTED_SUBSTRING="

try editing ${EXPECTED_SUBSTRING}\nGot -->\n${RESULT}" 78 | exit 1 79 | fi 80 | 81 | echo "${INDICATOR}Test GET SSR route '/sverdle'" 82 | EXPECTED_SUBSTRING='>Sverdle

' 83 | RESULT="$(curl -sL localhost:${PORT}/sverdle)" 84 | 85 | if [[ "${RESULT}" != *"${EXPECTED_SUBSTRING}"* ]]; then 86 | echo "${INDICATOR}Failed testing localhost:${PORT}/sverdle/" 87 | echo -e "Expect --> ${EXPECTED_SUBSTRING}\nGot -->\n${RESULT}" 88 | exit 1 89 | fi 90 | 91 | echo "${INDICATOR}Test POST to '/sverdle' API" 92 | EXPECTED_SUBSTRING='{"type":"success","status":204,"data":"-1"}' 93 | # expected result = {"uid":"","created_at":01234,"text":"asdf","done":false} 94 | # generated from the browser & copied with 'copy for cURL' browser context menu 95 | RESULT="$(curl -sL -X POST "http://localhost:${PORT}/sverdle?/enter" \ 96 | -H 'Accept-Language: en-US,en;q=0.9,vi;q=0.8' \ 97 | -H 'Content-Type: multipart/form-data; boundary=----wwkFyd1Rd5w6wAB4' \ 98 | -H "Origin: http://localhost:${PORT}" \ 99 | -H "User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:91.0) Gecko/20100101 Firefox/91.0" \ 100 | -H 'accept: application/json' \ 101 | -H 'x-sveltekit-action: true' \ 102 | --data-raw $'------wwkFyd1Rd5w6wAB4\r\nContent-Disposition: form-data; name="guess"\r\n\r\np\r\n------wwkFyd1Rd5w6wAB4\r\nContent-Disposition: form-data; name="guess"\r\n\r\na\r\n------wwkFyd1Rd5w6wAB4\r\nContent-Disposition: form-data; name="guess"\r\n\r\nr\r\n------wwkFyd1Rd5w6wAB4\r\nContent-Disposition: form-data; name="guess"\r\n\r\nt\r\n------wwkFyd1Rd5w6wAB4\r\nContent-Disposition: form-data; name="guess"\r\n\r\ny\r\n------wwkFyd1Rd5w6wAB4--\r\n')" 103 | if [[ "${RESULT}" != *"${EXPECTED_SUBSTRING}"* ]]; then 104 | echo "${INDICATOR}Failed POSTing to localhost:${PORT}/todos" 105 | echo -e "Expect --> ${EXPECTED_SUBSTRING}\nGot -->\n${RESULT}" 106 | exit 1 107 | fi 108 | echo "${INDICATOR}Success" 109 | -------------------------------------------------------------------------------- /tests/integration/functions_single_site/.firebaserc: -------------------------------------------------------------------------------- 1 | {"projects":{"default":"demo"}} 2 | -------------------------------------------------------------------------------- /tests/integration/functions_single_site/firebase.json: -------------------------------------------------------------------------------- 1 | { 2 | "hosting": { 3 | "public": "public", 4 | "target": "svelte-func-single-site", 5 | "rewrites": [ 6 | { 7 | "source": "**", 8 | "function": "sveltekit" 9 | } 10 | ] 11 | }, 12 | "functions": { 13 | "source": "functions" 14 | }, 15 | "emulators": { 16 | "ui": { 17 | "enabled": false 18 | }, 19 | "hosting": { 20 | "port": 8685 21 | }, 22 | "functions": { 23 | "port": 5001 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /tests/integration/functions_single_site/functions/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | 3 | # Specific to this app's firebase.json 4 | # ignore SvelteKit build output in Cloud Function 5 | sveltekit/ 6 | -------------------------------------------------------------------------------- /tests/integration/functions_single_site/functions/index.js: -------------------------------------------------------------------------------- 1 | const functions = require('firebase-functions'); 2 | 3 | let sveltekitServer; 4 | exports.sveltekit = functions.https.onRequest(async (request, response) => { 5 | if (!sveltekitServer) { 6 | functions.logger.info('Initialising SvelteKit SSR Handler'); 7 | sveltekitServer = require('./sveltekit/index').default; 8 | functions.logger.info('SvelteKit SSR Handler initialised!'); 9 | } 10 | 11 | functions.logger.info('Requested resource: ' + request.originalUrl); 12 | return sveltekitServer(request, response); 13 | }); 14 | -------------------------------------------------------------------------------- /tests/integration/functions_single_site/functions/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "functions", 3 | "description": "Cloud Functions for Firebase", 4 | "scripts": { 5 | "serve": "firebase emulators:start --only functions", 6 | "shell": "firebase functions:shell", 7 | "start": "npm run shell", 8 | "deploy": "firebase deploy --only functions", 9 | "logs": "firebase functions:log" 10 | }, 11 | "engines": { 12 | "node": "18" 13 | }, 14 | "main": "index.js", 15 | "dependencies": { 16 | "firebase-admin": "^11.3.0", 17 | "firebase-functions": "^4.1.1" 18 | }, 19 | "devDependencies": { 20 | "firebase-functions-test": "^3.0.0" 21 | }, 22 | "private": true 23 | } 24 | -------------------------------------------------------------------------------- /tests/integration/functions_single_site/svelte.config.js: -------------------------------------------------------------------------------- 1 | import firebase from 'svelte-adapter-firebase'; 2 | 3 | /** @type {import('@sveltejs/kit').Config} */ 4 | const config = { 5 | kit: { 6 | adapter: firebase(), 7 | } 8 | }; 9 | 10 | export default config; 11 | -------------------------------------------------------------------------------- /tests/integration/integration-test.bash: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # undefined vars are errors 4 | set -u 5 | IFS=$'\n\t' 6 | 7 | # Execute integration test for provided dir. Inits SvelteKit Todo app template 8 | # and adds Firebase Adapter with configuration from provided dir. 9 | # Assert build assets for static and compute are in expected locations 10 | # Requires deps in .tool-versions at repo root 11 | # 12 | # Usage: 13 | # 14 | # tests/integration/integration-test.bash "functions_single_site" "public/_app/immutable/pages/index.svelte-*.js" "functions/sveltekit/index.js" "." 15 | # tests/integration/integration-test.bash "nested_app_dirs" "public/_app/immutable/pages/index.svelte-*.js" "functions/sveltekit/index.js" "app" 16 | # tests/integration/integration-test.bash "run_service_id" "public/_app/immutable/pages/index.svelte-*.js" "functions/cloudrun/index.js" "." 17 | 18 | SOURCE_DIR="$1" 19 | PUBLIC_FILENAME="$2" 20 | KIT_FILENAME="$3" 21 | NESTED_APP_DIR="${4}" 22 | 23 | INDICATOR="====> " 24 | SCRIPT_PATH=$(dirname "$(realpath -s "$0")") 25 | TEST_DIR="$(mktemp -dt "svelte-adapter-firebase-test-${SOURCE_DIR}-XXXX")" 26 | 27 | # Cleanup files on exit 28 | # trap 'echo "${INDICATOR}Exiting, removing ${TEST_DIR}" && rm -rf -- "$TEST_DIR"' EXIT 29 | 30 | echo "${INDICATOR}TEST_DIR: ${TEST_DIR}" 31 | echo "${INDICATOR}PWD: ${PWD}" 32 | 33 | echo "${INDICATOR}Install svelte-adapter-firebase ${SCRIPT_PATH}/../../ deps" 34 | npm install 35 | 36 | echo "${INDICATOR}init SvelteKit Todos app" 37 | yes "" | "$(npm init svelte@next "${TEST_DIR}/${NESTED_APP_DIR}")" 38 | echo "${INDICATOR}Complete SvelteKit init" 39 | 40 | cp -R "${SCRIPT_PATH}"/"${SOURCE_DIR}"/* "${TEST_DIR}" 41 | cp "${SCRIPT_PATH}/${SOURCE_DIR}/.firebaserc" "${TEST_DIR}/.firebaserc" 42 | cp ".tool-versions" "${TEST_DIR}/.tool-versions" 43 | 44 | cd "${TEST_DIR}/${NESTED_APP_DIR}" || exit 1 45 | echo "${INDICATOR}PWD after cd to TEST_DIR: ${PWD}" 46 | 47 | echo "${INDICATOR}Set package.json:scripts.build to verbose mode" 48 | sed -i -e 's/svelte-kit build/svelte-kit build --verbose/g' "${TEST_DIR}/${NESTED_APP_DIR}/package.json" 49 | 50 | echo "${INDICATOR}Install kit template deps" 51 | npm install 52 | 53 | echo "${INDICATOR}Install svelte-adapter-firebase from ${SCRIPT_PATH}/../../" 54 | npm install "${SCRIPT_PATH}/../../" 55 | 56 | echo "${INDICATOR}Build Kit todos site" 57 | npm run build 58 | 59 | # Check ${PUBLIC_FILENAME} exists 60 | if ! [ $(ls ${TEST_DIR}/${NESTED_APP_DIR}/${PUBLIC_FILENAME} 2> /dev/null) ] ; then 61 | echo "${INDICATOR}FAILED to find ${TEST_DIR}/${NESTED_APP_DIR}/${PUBLIC_FILENAME}" 62 | exit 1 63 | fi 64 | 65 | # Check ${KIT_FILENAME} 66 | if [ ! -f "${TEST_DIR}/${KIT_FILENAME}" ]; then 67 | echo "${INDICATOR}FAILED to find ${TEST_DIR}/${KIT_FILENAME}" 68 | exit 1 69 | fi 70 | 71 | echo "${INDICATOR}Success" 72 | 73 | 74 | -------------------------------------------------------------------------------- /tests/integration/nested_app_dirs/.firebaserc: -------------------------------------------------------------------------------- 1 | {"projects":{"default":"demo"}} 2 | -------------------------------------------------------------------------------- /tests/integration/nested_app_dirs/app/svelte.config.js: -------------------------------------------------------------------------------- 1 | import firebase from 'svelte-adapter-firebase'; 2 | 3 | /** @type {import('@sveltejs/kit').Config} */ 4 | const config = { 5 | kit: { 6 | adapter: firebase({ 7 | target: 'svelte-func-single-site', 8 | firebaseJsonPath: '../firebase.json' 9 | }), 10 | } 11 | }; 12 | 13 | export default config; 14 | -------------------------------------------------------------------------------- /tests/integration/nested_app_dirs/firebase.json: -------------------------------------------------------------------------------- 1 | { 2 | "hosting": { 3 | "public": "app/public", 4 | "site": "nested-app-dir-site", 5 | "rewrites": [ 6 | { 7 | "source": "**", 8 | "function": "sveltekit" 9 | } 10 | ] 11 | }, 12 | "functions": { 13 | "source": "functions" 14 | }, 15 | "emulators": { 16 | "ui": { 17 | "enabled": false 18 | }, 19 | "hosting": { 20 | "port": 8685 21 | }, 22 | "functions": { 23 | "port": 5001 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /tests/integration/nested_app_dirs/functions/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | 3 | # Specific to this app's firebase.json 4 | # ignore SvelteKit build output in Cloud Function 5 | sveltekit/ 6 | -------------------------------------------------------------------------------- /tests/integration/nested_app_dirs/functions/index.js: -------------------------------------------------------------------------------- 1 | const functions = require('firebase-functions'); 2 | 3 | let sveltekitServer; 4 | exports.sveltekit = functions.https.onRequest(async (request, response) => { 5 | if (!sveltekitServer) { 6 | functions.logger.info('Initialising SvelteKit SSR Handler'); 7 | sveltekitServer = require('./sveltekit/index').default; 8 | functions.logger.info('SvelteKit SSR Handler initialised!'); 9 | } 10 | 11 | functions.logger.info('Requested resource: ' + request.originalUrl); 12 | return sveltekitServer(request, response); 13 | }); 14 | -------------------------------------------------------------------------------- /tests/integration/nested_app_dirs/functions/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "functions", 3 | "description": "Cloud Functions for Firebase", 4 | "scripts": { 5 | "serve": "firebase emulators:start --only functions", 6 | "shell": "firebase functions:shell", 7 | "start": "npm run shell", 8 | "deploy": "firebase deploy --only functions", 9 | "logs": "firebase functions:log" 10 | }, 11 | "engines": { 12 | "node": "18" 13 | }, 14 | "main": "index.js", 15 | "dependencies": { 16 | "firebase-admin": "^11.3.0", 17 | "firebase-functions": "^4.1.1" 18 | }, 19 | "devDependencies": { 20 | "firebase-functions-test": "^3.0.0" 21 | }, 22 | "private": true 23 | } 24 | -------------------------------------------------------------------------------- /tests/integration/run_service_id/.firebaserc: -------------------------------------------------------------------------------- 1 | {"projects":{"default":"demo"}} 2 | -------------------------------------------------------------------------------- /tests/integration/run_service_id/firebase.json: -------------------------------------------------------------------------------- 1 | { 2 | "hosting": { 3 | "public": "public", 4 | "site": "svelte-run-serviceid", 5 | "rewrites": [ 6 | { 7 | "source": "**", 8 | "run": { 9 | "serviceId": "cloudrun" 10 | } 11 | } 12 | ] 13 | }, 14 | "functions": { 15 | "source": "functions" 16 | }, 17 | "emulators": { 18 | "ui": { 19 | "enabled": false 20 | }, 21 | "hosting": { 22 | "port": 8685 23 | }, 24 | "functions": { 25 | "port": 5001 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /tests/integration/run_service_id/functions/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | 3 | # Specific to this app's firebase.json 4 | # ignore SvelteKit build output in Cloud Function 5 | sveltekit/ 6 | -------------------------------------------------------------------------------- /tests/integration/run_service_id/functions/index.js: -------------------------------------------------------------------------------- 1 | const functions = require('firebase-functions'); 2 | 3 | let cloudrunServer; 4 | exports.sveltekit = functions.https.onRequest(async (request, response) => { 5 | if (!cloudrunServer) { 6 | functions.logger.info('Initialising SvelteKit SSR Handler'); 7 | cloudrunServer = require('./cloudrun/index').default; 8 | functions.logger.info('SvelteKit SSR Handler initialised!'); 9 | } 10 | 11 | functions.logger.info('Requested resource: ' + request.originalUrl); 12 | return cloudrunServer(request, response); 13 | }); 14 | -------------------------------------------------------------------------------- /tests/integration/run_service_id/functions/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "functions", 3 | "description": "Cloud Functions for Firebase", 4 | "scripts": { 5 | "serve": "firebase emulators:start --only functions", 6 | "shell": "firebase functions:shell", 7 | "start": "npm run shell", 8 | "deploy": "firebase deploy --only functions", 9 | "logs": "firebase functions:log" 10 | }, 11 | "engines": { 12 | "node": "18" 13 | }, 14 | "main": "index.js", 15 | "dependencies": { 16 | "firebase-admin": "^11.3.0", 17 | "firebase-functions": "^4.1.1" 18 | }, 19 | "devDependencies": { 20 | "firebase-functions-test": "^3.0.0" 21 | }, 22 | "private": true 23 | } 24 | -------------------------------------------------------------------------------- /tests/integration/run_service_id/svelte.config.js: -------------------------------------------------------------------------------- 1 | import firebase from 'svelte-adapter-firebase'; 2 | 3 | /** @type {import('@sveltejs/kit').Config} */ 4 | const config = { 5 | kit: { 6 | adapter: firebase({target: 'svelte-func-single-site'}), 7 | } 8 | }; 9 | 10 | export default config; 11 | -------------------------------------------------------------------------------- /tests/unit/fixtures/failures/cf_invalid_function_name.json: -------------------------------------------------------------------------------- 1 | { 2 | "hosting": { 3 | "public": "app", 4 | "rewrites": [ 5 | { 6 | "source": "**", 7 | "function": "invalid-func-name" 8 | } 9 | ] 10 | }, 11 | "functions": { 12 | "source": "functions" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /tests/unit/fixtures/failures/cf_multi_site_no_target_match.json: -------------------------------------------------------------------------------- 1 | { 2 | "hosting": [ 3 | { 4 | "site": "app", 5 | "public": "app", 6 | "rewrites": [ 7 | { 8 | "source": "**", 9 | "function": "some_func" 10 | } 11 | ] 12 | }, 13 | { 14 | "site": "blog", 15 | "public": "blog" 16 | }, 17 | { 18 | "site": "marketing", 19 | "public": "marketing" 20 | } 21 | ], 22 | "functions": { 23 | "source": "functions" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /tests/unit/fixtures/failures/cf_multi_site_requires_target.json: -------------------------------------------------------------------------------- 1 | { 2 | "hosting": [ 3 | { 4 | "target": "app", 5 | "public": "app", 6 | "rewrites": [ 7 | { 8 | "source": "**", 9 | "function": "some_func" 10 | } 11 | ] 12 | }, 13 | { 14 | "site": "blog", 15 | "public": "blog" 16 | }, 17 | { 18 | "site": "marketing", 19 | "public": "marketing" 20 | } 21 | ], 22 | "functions": { 23 | "source": "functions" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /tests/unit/fixtures/failures/cf_site_missing_functions.json: -------------------------------------------------------------------------------- 1 | { 2 | "hosting": { 3 | "public": "app", 4 | "rewrites": [ 5 | { 6 | "source": "**", 7 | "function": "some_func" 8 | } 9 | ] 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /tests/unit/fixtures/failures/cf_site_rewrite_mismatch.json: -------------------------------------------------------------------------------- 1 | { 2 | "hosting": { 3 | "public": "app", 4 | "rewrites": [ 5 | { 6 | "source": "**", 7 | "function": "some_func" 8 | } 9 | ] 10 | }, 11 | "functions": { 12 | "source": "functions" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /tests/unit/fixtures/failures/cr_invalid_region.json: -------------------------------------------------------------------------------- 1 | { 2 | "hosting": { 3 | "public": "app", 4 | "rewrites": [ 5 | { 6 | "source": "**", 7 | "run": { 8 | "serviceId": "some-service", 9 | "region": "not-a-region" 10 | } 11 | } 12 | ] 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /tests/unit/fixtures/failures/cr_invalid_serviceId.json: -------------------------------------------------------------------------------- 1 | { 2 | "hosting": { 3 | "public": "not_empty", 4 | "rewrites": [ 5 | { 6 | "source": "**", 7 | "run": { 8 | "serviceId": "anInvalidServiceId" 9 | } 10 | } 11 | ] 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /tests/unit/fixtures/failures/cr_missing_serviceId.json: -------------------------------------------------------------------------------- 1 | { 2 | "hosting": { 3 | "public": "app", 4 | "rewrites": [ 5 | { 6 | "source": "**", 7 | "run": {} 8 | } 9 | ] 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /tests/unit/fixtures/failures/invalid.json: -------------------------------------------------------------------------------- 1 | { 2 | "hosting":{ 3 | "public": 4 | } 5 | } -------------------------------------------------------------------------------- /tests/unit/fixtures/failures/missing_hosting.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /tests/unit/fixtures/failures/site_empty_public.json: -------------------------------------------------------------------------------- 1 | { 2 | "hosting": { 3 | "public": "" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /tests/unit/fixtures/failures/site_missing_public.json: -------------------------------------------------------------------------------- 1 | { 2 | "hosting": {} 3 | } 4 | -------------------------------------------------------------------------------- /tests/unit/fixtures/failures/site_missing_rewrite.json: -------------------------------------------------------------------------------- 1 | { 2 | "hosting": { 3 | "public": "app" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /tests/unit/fixtures/failures/sites_missing_rewrites.json: -------------------------------------------------------------------------------- 1 | { 2 | "hosting": [ 3 | { 4 | "public": "some_dir" 5 | }, 6 | { 7 | "public": "another_dir" 8 | } 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /tests/unit/fixtures/successes/cf_site.json: -------------------------------------------------------------------------------- 1 | { 2 | "hosting": { 3 | "public": "app", 4 | "rewrites": [ 5 | { 6 | "source": "**", 7 | "function": "some_func" 8 | } 9 | ] 10 | }, 11 | "functions": { 12 | "source": "functions" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /tests/unit/fixtures/successes/cf_sites_w_site.json: -------------------------------------------------------------------------------- 1 | { 2 | "hosting": [ 3 | { 4 | "site": "app", 5 | "public": "app", 6 | "rewrites": [ 7 | { 8 | "source": "**", 9 | "function": "some_func" 10 | } 11 | ] 12 | }, 13 | { 14 | "site": "blog", 15 | "public": "blog" 16 | } 17 | ], 18 | "functions": { 19 | "source": "functions" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /tests/unit/fixtures/successes/cf_sites_w_target.json: -------------------------------------------------------------------------------- 1 | { 2 | "hosting": [ 3 | { 4 | "target": "app", 5 | "public": "app", 6 | "rewrites": [ 7 | { 8 | "source": "**", 9 | "function": "some_func" 10 | } 11 | ] 12 | }, 13 | { 14 | "target": "blog", 15 | "public": "blog" 16 | } 17 | ], 18 | "functions": { 19 | "source": "functions" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /tests/unit/fixtures/successes/cr_site.json: -------------------------------------------------------------------------------- 1 | { 2 | "hosting": { 3 | "public": "app", 4 | "rewrites": [ 5 | { 6 | "source": "**", 7 | "run": { 8 | "serviceId": "some-service" 9 | } 10 | } 11 | ] 12 | }, 13 | "functions": { 14 | "source": "functions" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /tests/unit/fixtures/successes/cr_sites_w_site.json: -------------------------------------------------------------------------------- 1 | { 2 | "hosting": [ 3 | { 4 | "site": "app", 5 | "public": "app", 6 | "rewrites": [ 7 | { 8 | "source": "**", 9 | "run": { 10 | "serviceId": "some-service" 11 | } 12 | } 13 | ] 14 | }, 15 | { 16 | "site": "blog", 17 | "public": "blog" 18 | } 19 | ], 20 | "functions": { 21 | "source": "functions" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /tests/unit/fixtures/successes/cr_sites_w_target.json: -------------------------------------------------------------------------------- 1 | { 2 | "hosting": [ 3 | { 4 | "target": "app", 5 | "public": "app", 6 | "rewrites": [ 7 | { 8 | "source": "**", 9 | "run": { 10 | "serviceId": "some-service" 11 | } 12 | } 13 | ] 14 | }, 15 | { 16 | "target": "blog", 17 | "public": "blog" 18 | } 19 | ], 20 | "functions": { 21 | "source": "functions" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /tests/unit/src/files/firebase-to-svelte-kit.test.js: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | 3 | import {toSvelteKitHeaders} from '../../../../src/files/firebase-to-svelte-kit.js'; 4 | 5 | // Headers 6 | test('leave headers without string[] untouched', t => { 7 | const input = { 8 | accept: 'something', 9 | 'accept-language': 'en', 10 | }; 11 | const expected = input; 12 | const result = toSvelteKitHeaders(input); 13 | 14 | t.deepEqual(result, expected); 15 | }); 16 | 17 | test('convert string[] headers to csv string values', t => { 18 | const expected = { 19 | accept: 'something', 20 | 'accept-language': 'en', 21 | 'set-cookie': 'some,cookie,data', 22 | }; 23 | const result = toSvelteKitHeaders({ 24 | accept: 'something', 25 | 'accept-language': 'en', 26 | 'set-cookie': ['some', 'cookie', 'data'], 27 | }); 28 | 29 | t.deepEqual(result, expected); 30 | }); 31 | 32 | test('convert string[] headers of any kind to csv string values', t => { 33 | const expected = { 34 | accept: 'something', 35 | 'accept-language': 'en', 36 | 'user-defined-header': 'some,user,defined,header,data', 37 | }; 38 | const result = toSvelteKitHeaders({ 39 | accept: 'something', 40 | 'accept-language': 'en', 41 | 'user-defined-header': ['some', 'user', 'defined', 'header', 'data'], 42 | }); 43 | 44 | t.deepEqual(result, expected); 45 | }); 46 | -------------------------------------------------------------------------------- /tests/unit/src/utils.test.js: -------------------------------------------------------------------------------- 1 | import {fileURLToPath} from 'url'; 2 | import path from 'path'; 3 | import test from 'ava'; 4 | import {ensureStaticResourceDirsDiffer, parseFirebaseConfiguration, validCloudFunctionName, validCloudRunServiceId} from '../../../src/utils.js'; 5 | 6 | // ParseFirebaseConfiguration: Valid configs 7 | test( 8 | 'Firebase config w Cloud Functions & single site', 9 | t => { 10 | const config = {target: undefined, sourceRewriteMatch: '**', firebaseJsonPath: fileURLToPath(new URL('../fixtures/successes/cf_site.json', import.meta.url))}; 11 | const result = parseFirebaseConfiguration(config); 12 | const expectedResult = {functions: {name: 'some_func', source: path.join(path.dirname(config.firebaseJsonPath), 'functions'), runtime: undefined}, publicDir: path.join(path.dirname(config.firebaseJsonPath), 'app')}; 13 | 14 | t.deepEqual(result, expectedResult); 15 | }, 16 | ); 17 | 18 | test( 19 | 'Firebase config w Cloud Functions & multiple sites using "firebase.json:hosting[].site" field', 20 | t => { 21 | const config = {target: 'app', sourceRewriteMatch: '**', firebaseJsonPath: fileURLToPath(new URL('../fixtures/successes/cf_sites_w_site.json', import.meta.url))}; 22 | const result = parseFirebaseConfiguration(config); 23 | const expectedResult = {functions: {name: 'some_func', source: path.join(path.dirname(config.firebaseJsonPath), 'functions'), runtime: undefined}, publicDir: path.join(path.dirname(config.firebaseJsonPath), 'app')}; 24 | 25 | t.deepEqual(result, expectedResult); 26 | }, 27 | ); 28 | 29 | test( 30 | 'Firebase config w Cloud Functions & multiple sites using "firebase.json:hosting[].target" field', 31 | t => { 32 | const config = {target: 'app', sourceRewriteMatch: '**', firebaseJsonPath: fileURLToPath(new URL('../fixtures/successes/cf_sites_w_target.json', import.meta.url))}; 33 | const result = parseFirebaseConfiguration(config); 34 | const expectedResult = {functions: {name: 'some_func', source: path.join(path.dirname(config.firebaseJsonPath), 'functions'), runtime: undefined}, publicDir: path.join(path.dirname(config.firebaseJsonPath), 'app')}; 35 | 36 | t.deepEqual(result, expectedResult); 37 | }, 38 | ); 39 | 40 | test( 41 | 'Firebase config w Cloud Run & single site', 42 | t => { 43 | const config = {target: undefined, sourceRewriteMatch: '**', firebaseJsonPath: fileURLToPath(new URL('../fixtures/successes/cr_site.json', import.meta.url))}; 44 | const result = parseFirebaseConfiguration(config); 45 | const expectedResult = {functions: {name: 'some-service', source: path.join(path.dirname(config.firebaseJsonPath), 'functions'), runtime: undefined}, publicDir: path.join(path.dirname(config.firebaseJsonPath), 'app')}; 46 | 47 | t.deepEqual(result, expectedResult); 48 | }, 49 | ); 50 | 51 | test( 52 | 'Firebase config w Cloud Run & multiple sites using "firebase.json:hosting[].site" field', 53 | t => { 54 | const config = {target: 'app', sourceRewriteMatch: '**', firebaseJsonPath: fileURLToPath(new URL('../fixtures/successes/cr_sites_w_site.json', import.meta.url))}; 55 | const result = parseFirebaseConfiguration(config); 56 | const expectedResult = {functions: {name: 'some-service', source: path.join(path.dirname(config.firebaseJsonPath), 'functions'), runtime: undefined}, publicDir: path.join(path.dirname(config.firebaseJsonPath), 'app')}; 57 | 58 | t.deepEqual(result, expectedResult); 59 | }, 60 | ); 61 | 62 | test( 63 | 'Firebase config w Cloud Run & multiple sites using "firebase.json:hosting[].target" field', 64 | t => { 65 | const config = {target: 'app', sourceRewriteMatch: '**', firebaseJsonPath: fileURLToPath(new URL('../fixtures/successes/cr_sites_w_target.json', import.meta.url))}; 66 | const result = parseFirebaseConfiguration(config); 67 | const expectedResult = {functions: {name: 'some-service', source: path.join(path.dirname(config.firebaseJsonPath), 'functions'), runtime: undefined}, publicDir: path.join(path.dirname(config.firebaseJsonPath), 'app')}; 68 | 69 | t.deepEqual(result, expectedResult); 70 | }, 71 | ); 72 | 73 | // ParseFirebaseConfiguration: Invalid configs 74 | test( 75 | 'Firebase config does not exist', 76 | t => { 77 | const firebaseJsonPath = fileURLToPath(new URL('does_not_exist.json', import.meta.url)); 78 | const config = {target: undefined, sourceRewriteMatch: '**', firebaseJsonPath}; 79 | t.throws( 80 | () => parseFirebaseConfiguration(config), 81 | {message: `Error: The adapter requires a "firebase.json" file. "firebaseJsonPath:${fileURLToPath(new URL('../src/does_not_exist.json', import.meta.url))}" does not exist.`}); 82 | }, 83 | ); 84 | 85 | test( 86 | 'Firebase config is invalid json', 87 | t => { 88 | const firebaseJsonPath = fileURLToPath(new URL('../fixtures/failures/invalid.json', import.meta.url)); 89 | const config = {target: undefined, sourceRewriteMatch: '**', firebaseJsonPath}; 90 | t.throws( 91 | () => parseFirebaseConfiguration(config), 92 | {message: `Error: failure while parsing ${fileURLToPath(new URL('../fixtures/failures/invalid.json', import.meta.url))}. Unexpected token } in JSON at position 28`}); 93 | }, 94 | ); 95 | 96 | test( 97 | 'Firebase config without "hosting" field', 98 | t => { 99 | const firebaseJsonPath = fileURLToPath(new URL('../fixtures/failures/missing_hosting.json', import.meta.url)); 100 | const config = {target: undefined, sourceRewriteMatch: '**', firebaseJsonPath}; 101 | t.throws( 102 | () => parseFirebaseConfiguration(config), 103 | {message: 'Error: "hosting" config missing from "firebase.json"'}); 104 | }, 105 | ); 106 | 107 | test( 108 | 'Firebase config w multiple sites missing "site" or "target" identifier', 109 | t => { 110 | const firebaseJsonPath = fileURLToPath(new URL('../fixtures/failures/sites_missing_rewrites.json', import.meta.url)); 111 | const config = {target: undefined, sourceRewriteMatch: '**', firebaseJsonPath}; 112 | t.throws( 113 | () => parseFirebaseConfiguration(config), 114 | {message: 'Error: Multiple "hosting" configurations found, each requires either a "site" field or "target" field, one does not. https://firebase.google.com/docs/hosting/multisites'}); 115 | }, 116 | ); 117 | 118 | test( 119 | 'Firebase config w multiple sites requires a "svelte.config.js:target" field to be specified', 120 | t => { 121 | const firebaseJsonPath = fileURLToPath(new URL('../fixtures/failures/cf_multi_site_requires_target.json', import.meta.url)); 122 | const config = {target: undefined, sourceRewriteMatch: '**', firebaseJsonPath}; 123 | t.throws( 124 | () => parseFirebaseConfiguration(config), 125 | {message: 'Error: Multiple "hosting" configurations found, but no "target" specified in "svelte.config.js" adapter config. Provide one so we can match the config correctly.'}); 126 | }, 127 | ); 128 | 129 | test( 130 | 'Firebase config w multiple sites but no match found for a "target" specified in svelte.config.js adapter config', 131 | t => { 132 | const firebaseJsonPath = fileURLToPath(new URL('../fixtures/failures/cf_multi_site_requires_target.json', import.meta.url)); 133 | const config = {target: 'no_matching_site', sourceRewriteMatch: '**', firebaseJsonPath}; 134 | t.throws( 135 | () => parseFirebaseConfiguration(config), 136 | {message: 'Error: Multiple "hosting" configurations found in "firebase.json" but not match found for no_matching_site specified in "svelte.config.js" adapter config. "hosting[].site" & "hosting[].target" values [{"target":"app"},{"site":"blog"},{"site":"marketing"}]'}); 137 | }, 138 | ); 139 | 140 | test( 141 | 'Firebase config w missing "public"', 142 | t => { 143 | const firebaseJsonPath = fileURLToPath(new URL('../fixtures/failures/site_missing_public.json', import.meta.url)); 144 | const config = {target: undefined, sourceRewriteMatch: '**', firebaseJsonPath}; 145 | t.throws( 146 | () => parseFirebaseConfiguration(config), 147 | {message: 'Error: Required "hosting.public" field not found for hosting configuration.'}); 148 | }, 149 | ); 150 | 151 | test('Firebase config w empty "public" string', t => { 152 | const firebaseJsonPath = fileURLToPath(new URL('../fixtures/failures/site_empty_public.json', import.meta.url)); 153 | const config = {sourceRewriteMatch: '**', firebaseJsonPath}; 154 | t.throws( 155 | () => parseFirebaseConfiguration(config), 156 | {message: 'Error: Required "hosting.public" field is an empty string, a directory is required.'}); 157 | }); 158 | 159 | test( 160 | 'Firebase config w site missing "rewrites"', 161 | t => { 162 | const firebaseJsonPath = fileURLToPath(new URL('../fixtures/failures/site_missing_rewrite.json', import.meta.url)); 163 | const config = {target: undefined, sourceRewriteMatch: '**', firebaseJsonPath}; 164 | t.throws( 165 | () => parseFirebaseConfiguration(config), 166 | {message: 'Error: Required "hosting[].rewrites" field not found for matched hosting configuration. Specify your Cloud Function with rewrite rule matching "source":"**"'}); 167 | }, 168 | ); 169 | 170 | test( 171 | 'Firebase config w "rewrites" mismatch', 172 | t => { 173 | const firebaseJsonPath = fileURLToPath(new URL('../fixtures/failures/cf_site_rewrite_mismatch.json', import.meta.url)); 174 | const config = {target: undefined, sourceRewriteMatch: 'no_match', firebaseJsonPath}; 175 | t.throws( 176 | () => parseFirebaseConfiguration(config), 177 | {message: 'Error: Required "hosting[].rewrites[]" does not contain a config with "source":"no_match" and either "function":"" or "run":{...} entries'}); 178 | }, 179 | ); 180 | 181 | test( 182 | 'Firebase config w Cloud Run missing required "serviceId" field', 183 | t => { 184 | const firebaseJsonPath = fileURLToPath(new URL('../fixtures/failures/cr_missing_serviceId.json', import.meta.url)); 185 | const config = {target: undefined, sourceRewriteMatch: '**', firebaseJsonPath}; 186 | t.throws( 187 | () => parseFirebaseConfiguration(config), 188 | {message: 'Error: Required "serviceId" field not found for Cloud Run rewrite rule in "firebase.json"'}); 189 | }, 190 | ); 191 | 192 | test( 193 | 'Firebase config w Cloud Run incompatible serviceId field', 194 | t => { 195 | const firebaseJsonPath = fileURLToPath(new URL('../fixtures/failures/cr_invalid_serviceId.json', import.meta.url)); 196 | const config = {target: undefined, sourceRewriteMatch: '**', firebaseJsonPath}; 197 | t.throws( 198 | () => parseFirebaseConfiguration(config), 199 | {message: 'Error: Cloud Run "serviceId" must use only lowercase alphanumeric characters and dashes cannot begin or end with a dash, and cannot be longer than 63 characters.'}); 200 | }, 201 | ); 202 | 203 | test( 204 | 'Firebase config w Cloud Run incompatible region field', 205 | t => { 206 | const firebaseJsonPath = fileURLToPath(new URL('../fixtures/failures/cr_invalid_region.json', import.meta.url)); 207 | const config = {target: undefined, sourceRewriteMatch: '**', firebaseJsonPath}; 208 | t.throws( 209 | () => parseFirebaseConfiguration(config), 210 | {message: 'Error: Cloud Run "region" is invalid, it should be "use-west1".'}); 211 | }, 212 | ); 213 | 214 | test( 215 | 'Firebase config w Cloud Function invalid name', 216 | t => { 217 | const firebaseJsonPath = fileURLToPath(new URL('../fixtures/failures/cf_invalid_function_name.json', import.meta.url)); 218 | const config = {target: undefined, sourceRewriteMatch: '**', firebaseJsonPath}; 219 | t.throws( 220 | () => parseFirebaseConfiguration(config), 221 | {message: 'Error: Cloud Function name must use only alphanumeric characters and underscores and cannot be longer than 63 characters'}); 222 | }, 223 | ); 224 | 225 | test( 226 | 'Firebase config w Cloud Functions & single site missing top-level functions', 227 | t => { 228 | const firebaseJsonPath = fileURLToPath(new URL('../fixtures/failures/cf_site_missing_functions.json', import.meta.url)); 229 | const config = {target: undefined, sourceRewriteMatch: '**', firebaseJsonPath}; 230 | t.throws( 231 | () => parseFirebaseConfiguration(config), 232 | {message: 'Error: Required "functions.source" field is missing from Firebase Configuration file.'}); 233 | }, 234 | ); 235 | 236 | // ValidCloudRunServiceId 237 | test('Cloud Run serviceId with all valid char types', t => { 238 | const result = validCloudRunServiceId('is-valid1'); 239 | t.is(result, true); 240 | }); 241 | 242 | test('Cloud Run serviceId with invalid dash prefix', t => { 243 | const result = validCloudRunServiceId('-not-valid1'); 244 | t.is(result, false); 245 | }); 246 | 247 | test('Cloud Run serviceId with invalid dash suffix', t => { 248 | const result = validCloudRunServiceId('not-valid1-'); 249 | t.is(result, false); 250 | }); 251 | 252 | test('Cloud Run serviceId with invalid uppercase char', t => { 253 | const result = validCloudRunServiceId('notValid1'); 254 | t.is(result, false); 255 | }); 256 | 257 | test('Cloud Run serviceId with invalid non-dash ($) symbol', t => { 258 | const result = validCloudRunServiceId('not$valid1'); 259 | t.is(result, false); 260 | }); 261 | 262 | test('Cloud Run serviceId with invalid non-dash (_) symbol', t => { 263 | const result = validCloudRunServiceId('not_valid1'); 264 | t.is(result, false); 265 | }); 266 | 267 | test('Cloud Run serviceId with invalid length', t => { 268 | const result = validCloudRunServiceId('aCloudFunctionsFunctionNameThatIsSeventyFiveCharactersLongWhichIsMoreThan63'); 269 | t.is(result, false); 270 | }); 271 | 272 | // ValidCloudFunctionName 273 | test('Cloud Function name with valid chars', t => { 274 | const result = validCloudFunctionName('lowercase_UPPERCASE_0123456789'); 275 | t.is(result, true); 276 | }); 277 | 278 | test('Cloud Function name with invalid dash', t => { 279 | const result = validCloudFunctionName('is-invalid'); 280 | t.is(result, false); 281 | }); 282 | 283 | test('Cloud Function name with invalid length', t => { 284 | const result = validCloudFunctionName('aCloudFunctionsFunctionNameThatIsSeventyFiveCharactersLongWhichIsMoreThan63'); 285 | t.is(result, false); 286 | }); 287 | 288 | // EnsureStaticResourceDirsDiffer 289 | test('Static asset source and dest different dirs', t => { 290 | t.notThrows( 291 | () => ensureStaticResourceDirsDiffer({source: 'a', dest: 'b'}), 292 | 'Error: Required "functions.source" field is missing from Firebase Configuration file.', 293 | ); 294 | }); 295 | 296 | test( 297 | 'Static asset source and dest the same dir', 298 | t => { 299 | t.throws( 300 | () => ensureStaticResourceDirsDiffer({source: 'a', dest: 'a'}), 301 | {message: 'Error: "firebase.json:hosting.public" field (a) must be a different directory to "svelte.config.js:kit.files.assets" field (a).'}, 302 | ); 303 | }, 304 | ); 305 | --------------------------------------------------------------------------------