├── .node-version
├── .npmrc
├── public
├── favicon.png
├── staticwebapp.config.json
└── index.html
├── .funcignore
├── proxies.json
├── remix.env.d.ts
├── .vscode
├── extensions.json
├── launch.json
├── settings.json
└── tasks.json
├── app
├── entry.client.tsx
├── styles
│ ├── index.css
│ └── global.css
├── routes
│ ├── 404.tsx
│ ├── page-2.tsx
│ └── index.tsx
├── entry.server.tsx
└── root.tsx
├── remix.config.js
├── README.md
├── azure
├── function
│ ├── index.js
│ └── handler.js
└── function.json
├── host.json
├── tsconfig.json
├── package.json
├── .github
└── workflows
│ ├── azure-static-web-apps-yellow-sky-07aa3e70f.yml
│ └── azure-static-web-apps-ashy-grass-090f92903.yml
└── .gitignore
/.node-version:
--------------------------------------------------------------------------------
1 | 14
2 |
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | @remix-run:registry=https://npm.remix.run
2 |
--------------------------------------------------------------------------------
/public/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mcansh/remix-on-azure/HEAD/public/favicon.png
--------------------------------------------------------------------------------
/.funcignore:
--------------------------------------------------------------------------------
1 | *.js.map
2 | *.ts
3 | .git*
4 | .vscode
5 | local.settings.json
6 | test
7 | tsconfig.json
--------------------------------------------------------------------------------
/proxies.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "http://json.schemastore.org/proxies",
3 | "proxies": {}
4 | }
5 |
--------------------------------------------------------------------------------
/remix.env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": [
3 | "ms-azuretools.vscode-azurefunctions"
4 | ]
5 | }
6 |
--------------------------------------------------------------------------------
/app/entry.client.tsx:
--------------------------------------------------------------------------------
1 | import ReactDOM from "react-dom";
2 | import { RemixBrowser } from "remix";
3 |
4 | ReactDOM.hydrate(, document);
5 |
--------------------------------------------------------------------------------
/app/styles/index.css:
--------------------------------------------------------------------------------
1 | /*
2 | * when the user visits this page, this style will apply, when they leave, it
3 | * will get unloaded, so don't worry so much about conflicting styles between
4 | * pages!
5 | */
6 |
--------------------------------------------------------------------------------
/remix.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | appDirectory: "app",
3 | browserBuildDirectory: "public/build",
4 | publicPath: "/build/",
5 | serverBuildDirectory: "azure/function/build",
6 | devServerPort: 8002,
7 | };
8 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Welcome to Remix!
2 |
3 | - [Remix Docs](https://docs.remix.run)
4 | - [Customer Dashboard](https://remix.run/dashboard)
5 |
6 | ## Development
7 |
8 | From your terminal:
9 |
10 | ```sh
11 | npm run dev
12 | ```
13 |
--------------------------------------------------------------------------------
/app/styles/global.css:
--------------------------------------------------------------------------------
1 | :focus:not(:focus-visible) {
2 | outline: none;
3 | }
4 |
5 | body {
6 | font-family: sans-serif;
7 | }
8 |
9 | footer {
10 | text-align: center;
11 | color: #ccc;
12 | padding-top: 80px;
13 | }
14 |
--------------------------------------------------------------------------------
/azure/function/index.js:
--------------------------------------------------------------------------------
1 | const { installGlobals } = require("@remix-run/node");
2 | const { createRequestHandler } = require("./handler");
3 |
4 | installGlobals();
5 |
6 | module.exports = createRequestHandler({ build: require("./build") });
7 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "0.2.0",
3 | "configurations": [
4 | {
5 | "name": "Attach to Node Functions",
6 | "type": "node",
7 | "request": "attach",
8 | "port": 9229,
9 | "preLaunchTask": "func: host start"
10 | }
11 | ]
12 | }
13 |
--------------------------------------------------------------------------------
/app/routes/404.tsx:
--------------------------------------------------------------------------------
1 | import type { MetaFunction } from "remix";
2 |
3 | export let meta: MetaFunction = () => {
4 | return { title: "Ain't nothing here" };
5 | };
6 |
7 | export default function FourOhFour() {
8 | return (
9 |
10 |
404
11 |
12 | );
13 | }
14 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "azureFunctions.deploySubpath": ".",
3 | "azureFunctions.postDeployTask": "npm install (functions)",
4 | "azureFunctions.projectLanguage": "TypeScript",
5 | "azureFunctions.projectRuntime": "~3",
6 | "debug.internalConsoleOptions": "neverOpen",
7 | "azureFunctions.preDeployTask": "npm prune (functions)"
8 | }
9 |
--------------------------------------------------------------------------------
/azure/function.json:
--------------------------------------------------------------------------------
1 | {
2 | "bindings": [
3 | {
4 | "authLevel": "anonymous",
5 | "type": "httpTrigger",
6 | "direction": "in",
7 | "name": "req"
8 | },
9 | {
10 | "type": "http",
11 | "direction": "out",
12 | "name": "$return"
13 | }
14 | ],
15 | "scriptFile": "./function/index.js"
16 | }
17 |
--------------------------------------------------------------------------------
/public/staticwebapp.config.json:
--------------------------------------------------------------------------------
1 | {
2 | "platform": {
3 | "apiRuntime": "node:16"
4 | },
5 | "routes": [
6 | {
7 | "route": "/",
8 | "rewrite": "/api/azure"
9 | },
10 | {
11 | "route": "/index.html",
12 | "rewrite": "/api/azure"
13 | }
14 | ],
15 | "navigationFallback": {
16 | "rewrite": "/api/azure"
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/app/routes/page-2.tsx:
--------------------------------------------------------------------------------
1 | import { RouteComponent, MetaFunction, Link } from "remix";
2 |
3 | const meta: MetaFunction = () => ({
4 | title: "Page 2",
5 | });
6 |
7 | const Page: RouteComponent = () => {
8 | return (
9 | <>
10 | Welcome to Page 2👋
11 | Go back home.
12 | >
13 | );
14 | };
15 |
16 | export default Page;
17 | export { meta };
18 |
--------------------------------------------------------------------------------
/host.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "2.0",
3 | "logging": {
4 | "applicationInsights": {
5 | "samplingSettings": {
6 | "isEnabled": true,
7 | "excludedTypes": "Request"
8 | }
9 | }
10 | },
11 | "extensions": {
12 | "http": {
13 | "routePrefix": "api"
14 | }
15 | },
16 | "extensionBundle": {
17 | "id": "Microsoft.Azure.Functions.ExtensionBundle",
18 | "version": "[2.*, 3.0.0)"
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Remix
8 |
9 |
10 |
11 | This is a placeholder file you should never see, we'll rewrite all
12 | requests to our azure function
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "include": ["remix.env.d.ts", "**/*.ts", "**/*.tsx"],
3 | "compilerOptions": {
4 | "forceConsistentCasingInFileNames": true,
5 | "lib": ["DOM", "DOM.Iterable", "ES2019"],
6 | "isolatedModules": true,
7 | "esModuleInterop": true,
8 | "jsx": "react-jsx",
9 | "moduleResolution": "node",
10 | "resolveJsonModule": true,
11 | "target": "ES2019",
12 | "strict": true,
13 | "baseUrl": ".",
14 | "paths": {
15 | "~/*": ["./app/*"]
16 | },
17 |
18 | // Remix takes care of building everything in `remix build`.
19 | "noEmit": true
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/app/entry.server.tsx:
--------------------------------------------------------------------------------
1 | import ReactDOMServer from "react-dom/server";
2 | import type { EntryContext } from "remix";
3 | import { RemixServer } from "remix";
4 |
5 | export default function handleRequest(
6 | request: Request,
7 | responseStatusCode: number,
8 | responseHeaders: Headers,
9 | remixContext: EntryContext
10 | ) {
11 | let markup = ReactDOMServer.renderToString(
12 |
13 | );
14 |
15 | responseHeaders.set("Content-Type", "text/html");
16 |
17 | return new Response("" + markup, {
18 | status: responseStatusCode,
19 | headers: responseHeaders,
20 | });
21 | }
22 |
--------------------------------------------------------------------------------
/.vscode/tasks.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "2.0.0",
3 | "tasks": [
4 | {
5 | "type": "func",
6 | "command": "host start",
7 | "problemMatcher": "$func-node-watch",
8 | "isBackground": true,
9 | "dependsOn": "npm build (functions)"
10 | },
11 | {
12 | "type": "shell",
13 | "label": "npm build (functions)",
14 | "command": "npm run build",
15 | "dependsOn": "npm install (functions)",
16 | "problemMatcher": "$tsc"
17 | },
18 | {
19 | "type": "shell",
20 | "label": "npm install (functions)",
21 | "command": "npm install"
22 | },
23 | {
24 | "type": "shell",
25 | "label": "npm prune (functions)",
26 | "command": "npm prune --production",
27 | "dependsOn": "npm build (functions)",
28 | "problemMatcher": []
29 | }
30 | ]
31 | }
32 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "name": "remix-on-azure",
4 | "description": "",
5 | "license": "",
6 | "scripts": {
7 | "build": "remix build",
8 | "dev": "cross-env NODE_ENV=development swa start ./public --run \"remix dev\" --api-location .",
9 | "start": "remix-serve build",
10 | "prepare": "remix setup node"
11 | },
12 | "dependencies": {
13 | "@remix-run/dev": "1.1.3",
14 | "@remix-run/node": "1.1.3",
15 | "@remix-run/react": "1.1.3",
16 | "@remix-run/serve": "1.1.3",
17 | "react": "17.0.2",
18 | "react-dom": "17.0.2",
19 | "remix": "1.1.3"
20 | },
21 | "devDependencies": {
22 | "@azure/functions": "3.0.0",
23 | "@azure/static-web-apps-cli": "0.8.2",
24 | "@types/react": "17.0.38",
25 | "@types/react-dom": "17.0.11",
26 | "cross-env": "7.0.3",
27 | "typescript": "4.5.4"
28 | },
29 | "engines": {
30 | "node": ">=14"
31 | },
32 | "sideEffects": false
33 | }
34 |
--------------------------------------------------------------------------------
/app/routes/index.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | Link,
3 | LinksFunction,
4 | LoaderFunction,
5 | MetaFunction,
6 | useLoaderData,
7 | } from "remix";
8 |
9 | import stylesUrl from "../styles/index.css";
10 |
11 | export let meta: MetaFunction = () => {
12 | return {
13 | title: "Remix Starter",
14 | description: "Welcome to remix!",
15 | };
16 | };
17 |
18 | export let links: LinksFunction = () => {
19 | return [{ rel: "stylesheet", href: stylesUrl }];
20 | };
21 |
22 | export let loader: LoaderFunction = async () => {
23 | return { message: "this is awesome 😎" };
24 | };
25 |
26 | export default function Index() {
27 | let data = useLoaderData();
28 |
29 | return (
30 |
31 |
Welcome to Remix on Azure!
32 |
Go to page 2
33 |
34 | Check out the docs to get started.
35 |
36 |
Message from the loader: {data.message}
37 |
38 | );
39 | }
40 |
--------------------------------------------------------------------------------
/app/root.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | Links,
3 | LinksFunction,
4 | LiveReload,
5 | LoaderFunction,
6 | Meta,
7 | Outlet,
8 | Scripts,
9 | ScrollRestoration,
10 | useLoaderData,
11 | } from "remix";
12 |
13 | import stylesUrl from "./styles/global.css";
14 |
15 | export let links: LinksFunction = () => {
16 | return [{ rel: "stylesheet", href: stylesUrl }];
17 | };
18 |
19 | export let loader: LoaderFunction = async () => {
20 | return { date: new Date() };
21 | };
22 |
23 | function Document({ children }: { children: React.ReactNode }) {
24 | return (
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 | {children}
34 |
35 |
36 |
37 |
38 |
39 | );
40 | }
41 |
42 | export default function App() {
43 | let data = useLoaderData();
44 | return (
45 |
46 |
47 |
50 |
51 | );
52 | }
53 |
54 | export function ErrorBoundary({ error }: { error: Error }) {
55 | return (
56 |
57 | App Error
58 | {error.message}
59 |
60 | Replace this UI with what you want users to see when your app throws
61 | uncaught errors.
62 |
63 |
64 | );
65 | }
66 |
--------------------------------------------------------------------------------
/azure/function/handler.js:
--------------------------------------------------------------------------------
1 | const {
2 | createRequestHandler: createRemixRequestHandler,
3 | } = require("@remix-run/server-runtime");
4 |
5 | const {
6 | formatServerError,
7 | Headers: NodeHeaders,
8 | Request: NodeRequest,
9 | Response: NodeResponse,
10 | RequestInit: NodeRequestInit,
11 | } = require("@remix-run/node");
12 |
13 | function createRequestHandler({
14 | build,
15 | getLoadContext,
16 | mode = process.env.NODE_ENV,
17 | }) {
18 | let platform = { formatServerError };
19 | let handleRequest = createRemixRequestHandler(build, platform, mode);
20 |
21 | return async (_context, req) => {
22 | let request = createRemixRequest(req);
23 | let loadContext =
24 | typeof getLoadContext === "function" ? getLoadContext(req) : undefined;
25 |
26 | let response = await handleRequest(request, loadContext);
27 |
28 | return {
29 | status: response.status,
30 | headers: response.headers.raw(),
31 | body: await response.text(),
32 | };
33 | };
34 | }
35 |
36 | function createRemixHeaders(requestHeaders) {
37 | let headers = new NodeHeaders();
38 |
39 | for (let [key, value] of Object.entries(requestHeaders)) {
40 | if (!value) continue;
41 | headers.set(key, value);
42 | }
43 |
44 | return headers;
45 | }
46 |
47 | function createRemixRequest(req) {
48 | let url = req.headers["x-ms-original-url"];
49 |
50 | let init = {
51 | method: req.method || "GET",
52 | headers: createRemixHeaders(req.headers),
53 | };
54 |
55 | if (req.body && req.method !== "GET" && req.method !== "HEAD") {
56 | init.body = req.body;
57 | }
58 |
59 | return new NodeRequest(url, init);
60 | }
61 |
62 | module.exports = { createRequestHandler };
63 |
--------------------------------------------------------------------------------
/.github/workflows/azure-static-web-apps-yellow-sky-07aa3e70f.yml:
--------------------------------------------------------------------------------
1 | name: Azure Static Web Apps CI/CD
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 | pull_request:
8 | types: [opened, synchronize, reopened, closed]
9 | branches:
10 | - main
11 |
12 | jobs:
13 | build_and_deploy_job:
14 | if: github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.action != 'closed')
15 | runs-on: ubuntu-latest
16 | name: Build and Deploy Job
17 | steps:
18 | - uses: actions/checkout@v2
19 | with:
20 | submodules: true
21 | - name: Build And Deploy
22 | id: builddeploy
23 | uses: Azure/static-web-apps-deploy@v1
24 | with:
25 | azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_YELLOW_SKY_07AA3E70F }}
26 | repo_token: ${{ secrets.GITHUB_TOKEN }} # Used for Github integrations (i.e. PR comments)
27 | action: "upload"
28 | ###### Repository/Build Configurations - These values can be configured to match your app requirements. ######
29 | # For more information regarding Static Web App workflow configurations, please visit: https://aka.ms/swaworkflowconfig
30 | app_location: "/" # App source code path
31 | api_location: "/" # Api source code path - optional
32 | output_location: "/public" # Built app content directory - optional
33 | ###### End of Repository/Build Configurations ######
34 |
35 | close_pull_request_job:
36 | if: github.event_name == 'pull_request' && github.event.action == 'closed'
37 | runs-on: ubuntu-latest
38 | name: Close Pull Request Job
39 | steps:
40 | - name: Close Pull Request
41 | id: closepullrequest
42 | uses: Azure/static-web-apps-deploy@v1
43 | with:
44 | azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_YELLOW_SKY_07AA3E70F }}
45 | action: "close"
46 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | lerna-debug.log*
8 |
9 | # Diagnostic reports (https://nodejs.org/api/report.html)
10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
11 |
12 | # Runtime data
13 | pids
14 | *.pid
15 | *.seed
16 | *.pid.lock
17 |
18 | # Directory for instrumented libs generated by jscoverage/JSCover
19 | lib-cov
20 |
21 | # Coverage directory used by tools like istanbul
22 | coverage
23 |
24 | # nyc test coverage
25 | .nyc_output
26 |
27 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
28 | .grunt
29 |
30 | # Bower dependency directory (https://bower.io/)
31 | bower_components
32 |
33 | # node-waf configuration
34 | .lock-wscript
35 |
36 | # Compiled binary addons (https://nodejs.org/api/addons.html)
37 | build/Release
38 |
39 | # Dependency directories
40 | node_modules/
41 | jspm_packages/
42 |
43 | # TypeScript v1 declaration files
44 | typings/
45 |
46 | # Optional npm cache directory
47 | .npm
48 |
49 | # Optional eslint cache
50 | .eslintcache
51 |
52 | # Optional REPL history
53 | .node_repl_history
54 |
55 | # Output of 'npm pack'
56 | *.tgz
57 |
58 | # Yarn Integrity file
59 | .yarn-integrity
60 |
61 | # dotenv environment variables file
62 | .env
63 | .env.test
64 |
65 | # parcel-bundler cache (https://parceljs.org/)
66 | .cache
67 |
68 | # next.js build output
69 | .next
70 |
71 | # nuxt.js build output
72 | .nuxt
73 |
74 | # vuepress build output
75 | .vuepress/dist
76 |
77 | # Serverless directories
78 | .serverless/
79 |
80 | # FuseBox cache
81 | .fusebox/
82 |
83 | # DynamoDB Local files
84 | .dynamodb/
85 |
86 | # TypeScript output
87 | dist
88 | out
89 |
90 | # Azure Functions artifacts
91 | bin
92 | obj
93 | appsettings.json
94 | local.settings.json
95 |
96 | node_modules
97 |
98 | /.cache
99 | /azure/function/build
100 | /public/build
101 |
--------------------------------------------------------------------------------
/.github/workflows/azure-static-web-apps-ashy-grass-090f92903.yml:
--------------------------------------------------------------------------------
1 | name: Azure Static Web Apps CI/CD
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 | pull_request:
8 | types: [opened, synchronize, reopened, closed]
9 | branches:
10 | - main
11 |
12 | jobs:
13 | build_and_deploy_job:
14 | if: github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.action != 'closed')
15 | runs-on: ubuntu-latest
16 | name: Build and Deploy Job
17 | steps:
18 | - uses: actions/checkout@v2
19 | with:
20 | submodules: true
21 | - name: Build And Deploy
22 | id: builddeploy
23 | uses: Azure/static-web-apps-deploy@v1
24 | with:
25 | azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_ASHY_GRASS_090F92903 }}
26 | repo_token: ${{ secrets.GITHUB_TOKEN }} # Used for Github integrations (i.e. PR comments)
27 | action: "upload"
28 | ###### Repository/Build Configurations - These values can be configured to match your app requirements. ######
29 | # For more information regarding Static Web App workflow configurations, please visit: https://aka.ms/swaworkflowconfig
30 | app_location: "/" # App source code path
31 | api_location: "/" # Api source code path - optional
32 | output_location: "/public" # Built app content directory - optional
33 | ###### End of Repository/Build Configurations ######
34 |
35 | close_pull_request_job:
36 | if: github.event_name == 'pull_request' && github.event.action == 'closed'
37 | runs-on: ubuntu-latest
38 | name: Close Pull Request Job
39 | steps:
40 | - name: Close Pull Request
41 | id: closepullrequest
42 | uses: Azure/static-web-apps-deploy@v1
43 | with:
44 | azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_ASHY_GRASS_090F92903 }}
45 | action: "close"
46 |
--------------------------------------------------------------------------------