Skip to content

Commit c7debbf

Browse files
committed
[TestApps] Introduce an EncoreApp with all UX PHP and JS packages required
1 parent b536b09 commit c7debbf

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+9692
-0
lines changed

test_apps/encore-app/.env

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# In all environments, the following files are loaded if they exist,
2+
# the latter taking precedence over the former:
3+
#
4+
# * .env contains default values for the environment variables needed by the app
5+
# * .env.local uncommitted file with local overrides
6+
# * .env.$APP_ENV committed environment-specific defaults
7+
# * .env.$APP_ENV.local uncommitted environment-specific overrides
8+
#
9+
# Real environment variables win over .env files.
10+
#
11+
# DO NOT DEFINE PRODUCTION SECRETS IN THIS FILE NOR IN ANY OTHER COMMITTED FILES.
12+
# https://symfony.com/doc/current/configuration/secrets.html
13+
#
14+
# Run "composer dump-env prod" to compile .env files for production use (requires symfony/flex >=1.2).
15+
# https://symfony.com/doc/current/best_practices.html#use-environment-variables-for-infrastructure-configuration
16+
17+
###> symfony/framework-bundle ###
18+
APP_ENV=dev
19+
APP_SECRET=
20+
###< symfony/framework-bundle ###
21+
22+
###> symfony/ux-google-map ###
23+
# Options available at https://github.com/symfony/ux/blob/2.x/src/Map/src/Bridge/Google/README.md
24+
#
25+
GOOGLE_MAPS_API_KEY="# Get your API key at https://developers.google.com/maps/documentation/javascript/get-api-key"
26+
UX_MAP_DSN=google://%env(GOOGLE_MAPS_API_KEY)%@default
27+
###< symfony/ux-google-map ###
28+
29+
###> symfony/ux-leaflet-map ###
30+
# Options available at https://github.com/symfony/ux/blob/2.x/src/Map/src/Bridge/Leaflet/README.md
31+
#
32+
UX_MAP_DSN=leaflet://default
33+
###< symfony/ux-leaflet-map ###
34+
35+
###> symfony/mercure-notifier ###
36+
# MERCURE_DSN=mercure://default
37+
###< symfony/mercure-notifier ###
38+
39+
###> symfony/mercure-bundle ###
40+
# See https://symfony.com/doc/current/mercure.html#configuration
41+
# The URL of the Mercure hub, used by the app to publish updates (can be a local URL)
42+
MERCURE_URL=https://example.com/.well-known/mercure
43+
# The public URL of the Mercure hub, used by the browser to connect
44+
MERCURE_PUBLIC_URL=https://example.com/.well-known/mercure
45+
# The secret used to sign the JWTs
46+
MERCURE_JWT_SECRET="!ChangeThisMercureHubJWTSecretKey!"
47+
###< symfony/mercure-bundle ###

test_apps/encore-app/.env.dev

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
2+
###> symfony/framework-bundle ###
3+
APP_SECRET=ccb30b91aeb20e033fe10056ae0614e9
4+
###< symfony/framework-bundle ###

test_apps/encore-app/.gitignore

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
composer.lock
2+
3+
.yarn/*
4+
!.yarn/cache
5+
!.yarn/patches
6+
!.yarn/plugins
7+
!.yarn/releases
8+
!.yarn/sdks
9+
!.yarn/versions
10+
/node_modules
11+
yarn-error.log
12+
13+
###> symfony/framework-bundle ###
14+
/.env.local
15+
/.env.local.php
16+
/.env.*.local
17+
/config/secrets/prod/prod.decrypt.private.php
18+
/public/bundles/
19+
/var/
20+
/vendor/
21+
###< symfony/framework-bundle ###
22+
23+
###> symfony/webpack-encore-bundle ###
24+
/node_modules/
25+
/public/build/
26+
npm-debug.log
27+
yarn-error.log
28+
###< symfony/webpack-encore-bundle ###

test_apps/encore-app/assets/app.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/*
2+
* Welcome to your app's main JavaScript file!
3+
*
4+
* We recommend including the built version of this JavaScript file
5+
* (and its CSS file) in your base layout (base.html.twig).
6+
*/
7+
import { registerVueControllerComponents } from '@symfony/ux-vue';
8+
import { registerSvelteControllerComponents } from '@symfony/ux-svelte';
9+
import { registerReactControllerComponents } from '@symfony/ux-react';
10+
import './bootstrap.js';
11+
12+
// any CSS you import will output into a single css file (app.css in this case)
13+
import './styles/app.css';
14+
import { THIS_FIELD_IS_MISSING, trans } from './translator';
15+
16+
registerReactControllerComponents(require.context('./react/controllers', true, /\.(j|t)sx?$/));
17+
registerSvelteControllerComponents(require.context('./svelte/controllers', true, /\.svelte$/));
18+
registerVueControllerComponents(require.context('./vue/controllers', true, /\.vue$/));
19+
20+
document.addEventListener('DOMContentLoaded', () => {
21+
console.log(trans(THIS_FIELD_IS_MISSING));
22+
})
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { startStimulusApp } from '@symfony/stimulus-bridge';
2+
3+
// Registers Stimulus controllers from controllers.json and in the controllers/ directory
4+
export const app = startStimulusApp(require.context(
5+
'@symfony/stimulus-bridge/lazy-controller-loader!./controllers',
6+
true,
7+
/\.[jt]sx?$/
8+
));
9+
// register any custom, 3rd party controllers here
10+
// app.register('some_controller_name', SomeImportedController);
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
{
2+
"controllers": {
3+
"@symfony/ux-autocomplete": {
4+
"autocomplete": {
5+
"enabled": true,
6+
"fetch": "eager",
7+
"autoimport": {
8+
"tom-select/dist/css/tom-select.default.css": true,
9+
"tom-select/dist/css/tom-select.bootstrap4.css": false,
10+
"tom-select/dist/css/tom-select.bootstrap5.css": false
11+
}
12+
}
13+
},
14+
"@symfony/ux-chartjs": {
15+
"chart": {
16+
"enabled": true,
17+
"fetch": "eager"
18+
}
19+
},
20+
"@symfony/ux-cropperjs": {
21+
"cropper": {
22+
"enabled": true,
23+
"fetch": "eager",
24+
"autoimport": {
25+
"cropperjs/dist/cropper.min.css": true,
26+
"@symfony/ux-cropperjs/dist/style.min.css": true
27+
}
28+
}
29+
},
30+
"@symfony/ux-dropzone": {
31+
"dropzone": {
32+
"enabled": true,
33+
"fetch": "eager",
34+
"autoimport": {
35+
"@symfony/ux-dropzone/dist/style.min.css": true
36+
}
37+
}
38+
},
39+
"@symfony/ux-google-map": {
40+
"map": {
41+
"enabled": true,
42+
"fetch": "lazy"
43+
}
44+
},
45+
"@symfony/ux-lazy-image": {
46+
"lazy-image": {
47+
"enabled": true,
48+
"fetch": "eager"
49+
}
50+
},
51+
"@symfony/ux-leaflet-map": {
52+
"map": {
53+
"enabled": true,
54+
"fetch": "lazy"
55+
}
56+
},
57+
"@symfony/ux-live-component": {
58+
"live": {
59+
"enabled": true,
60+
"fetch": "eager",
61+
"autoimport": {
62+
"@symfony/ux-live-component/dist/live.min.css": true
63+
}
64+
}
65+
},
66+
"@symfony/ux-notify": {
67+
"notify": {
68+
"enabled": true,
69+
"fetch": "eager"
70+
}
71+
},
72+
"@symfony/ux-react": {
73+
"react": {
74+
"enabled": true,
75+
"fetch": "eager"
76+
}
77+
},
78+
"@symfony/ux-svelte": {
79+
"svelte": {
80+
"enabled": true,
81+
"fetch": "eager"
82+
}
83+
},
84+
"@symfony/ux-swup": {
85+
"swup": {
86+
"enabled": true,
87+
"fetch": "eager"
88+
}
89+
},
90+
"@symfony/ux-toggle-password": {
91+
"toggle-password": {
92+
"enabled": true,
93+
"fetch": "eager",
94+
"autoimport": {
95+
"@symfony/ux-toggle-password/dist/style.min.css": true
96+
}
97+
}
98+
},
99+
"@symfony/ux-turbo": {
100+
"turbo-core": {
101+
"enabled": true,
102+
"fetch": "eager"
103+
},
104+
"mercure-turbo-stream": {
105+
"enabled": false,
106+
"fetch": "eager"
107+
}
108+
},
109+
"@symfony/ux-typed": {
110+
"typed": {
111+
"enabled": true,
112+
"fetch": "eager"
113+
}
114+
},
115+
"@symfony/ux-vue": {
116+
"vue": {
117+
"enabled": true,
118+
"fetch": "eager"
119+
}
120+
}
121+
},
122+
"entrypoints": []
123+
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
const nameCheck = /^[-_a-zA-Z0-9]{4,22}$/;
2+
const tokenCheck = /^[-_\/+a-zA-Z0-9]{24,}$/;
3+
4+
// Generate and double-submit a CSRF token in a form field and a cookie, as defined by Symfony's SameOriginCsrfTokenManager
5+
document.addEventListener('submit', function (event) {
6+
generateCsrfToken(event.target);
7+
}, true);
8+
9+
// When @hotwired/turbo handles form submissions, send the CSRF token in a header in addition to a cookie
10+
// The `framework.csrf_protection.check_header` config option needs to be enabled for the header to be checked
11+
document.addEventListener('turbo:submit-start', function (event) {
12+
const h = generateCsrfHeaders(event.detail.formSubmission.formElement);
13+
Object.keys(h).map(function (k) {
14+
event.detail.formSubmission.fetchRequest.headers[k] = h[k];
15+
});
16+
});
17+
18+
// When @hotwired/turbo handles form submissions, remove the CSRF cookie once a form has been submitted
19+
document.addEventListener('turbo:submit-end', function (event) {
20+
removeCsrfToken(event.detail.formSubmission.formElement);
21+
});
22+
23+
export function generateCsrfToken (formElement) {
24+
const csrfField = formElement.querySelector('input[data-controller="csrf-protection"], input[name="_csrf_token"]');
25+
26+
if (!csrfField) {
27+
return;
28+
}
29+
30+
let csrfCookie = csrfField.getAttribute('data-csrf-protection-cookie-value');
31+
let csrfToken = csrfField.value;
32+
33+
if (!csrfCookie && nameCheck.test(csrfToken)) {
34+
csrfField.setAttribute('data-csrf-protection-cookie-value', csrfCookie = csrfToken);
35+
csrfField.defaultValue = csrfToken = btoa(String.fromCharCode.apply(null, (window.crypto || window.msCrypto).getRandomValues(new Uint8Array(18))));
36+
csrfField.dispatchEvent(new Event('change', { bubbles: true }));
37+
}
38+
39+
if (csrfCookie && tokenCheck.test(csrfToken)) {
40+
const cookie = csrfCookie + '_' + csrfToken + '=' + csrfCookie + '; path=/; samesite=strict';
41+
document.cookie = window.location.protocol === 'https:' ? '__Host-' + cookie + '; secure' : cookie;
42+
}
43+
}
44+
45+
export function generateCsrfHeaders (formElement) {
46+
const headers = {};
47+
const csrfField = formElement.querySelector('input[data-controller="csrf-protection"], input[name="_csrf_token"]');
48+
49+
if (!csrfField) {
50+
return headers;
51+
}
52+
53+
const csrfCookie = csrfField.getAttribute('data-csrf-protection-cookie-value');
54+
55+
if (tokenCheck.test(csrfField.value) && nameCheck.test(csrfCookie)) {
56+
headers[csrfCookie] = csrfField.value;
57+
}
58+
59+
return headers;
60+
}
61+
62+
export function removeCsrfToken (formElement) {
63+
const csrfField = formElement.querySelector('input[data-controller="csrf-protection"], input[name="_csrf_token"]');
64+
65+
if (!csrfField) {
66+
return;
67+
}
68+
69+
const csrfCookie = csrfField.getAttribute('data-csrf-protection-cookie-value');
70+
71+
if (tokenCheck.test(csrfField.value) && nameCheck.test(csrfCookie)) {
72+
const cookie = csrfCookie + '_' + csrfField.value + '=0; path=/; samesite=strict; max-age=0';
73+
74+
document.cookie = window.location.protocol === 'https:' ? '__Host-' + cookie + '; secure' : cookie;
75+
}
76+
}
77+
78+
/* stimulusFetch: 'lazy' */
79+
export default 'csrf-protection-controller';
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { Controller } from '@hotwired/stimulus';
2+
3+
/*
4+
* This is an example Stimulus controller!
5+
*
6+
* Any element with a data-controller="hello" attribute will cause
7+
* this controller to be executed. The name "hello" comes from the filename:
8+
* hello_controller.js -> "hello"
9+
*
10+
* Delete this file or adapt it for your use!
11+
*/
12+
export default class extends Controller {
13+
connect() {
14+
this.element.textContent = 'Hello Stimulus! Edit me in assets/controllers/hello_controller.js';
15+
}
16+
}
Lines changed: 1 addition & 0 deletions
Loading
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import React from 'react';
2+
3+
export default function (props) {
4+
return <div>Hello {props.fullName}</div>;
5+
}

0 commit comments

Comments
 (0)