Skip to content

Commit 273b403

Browse files
committed
feature #859 Implement Stimulus bridge configurator (tgalopin)
This PR was merged into the main branch. Discussion ---------- Implement Stimulus bridge configurator Implement Stimulus bridge configurator for Symfony UX Commits ------- c84cfb3 Implement Stimulus bridge configurator
2 parents f4d4e75 + c84cfb3 commit 273b403

File tree

16 files changed

+163
-0
lines changed

16 files changed

+163
-0
lines changed

fixtures/stimulus/assets/app.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import { startStimulusApp } from '@symfony/stimulus-bridge';
2+
import '@symfony/autoimport';
3+
4+
export const app = startStimulusApp(require.context('./controllers', true, /\.(j|t)sx?$/));
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"controllers": {
3+
"@symfony/mock-module": {
4+
"mock": {
5+
"webpackMode": "lazy",
6+
"enabled": true,
7+
"autoimport": {
8+
"@symfony/mock-module/dist/style.css": true
9+
}
10+
}
11+
}
12+
},
13+
"entrypoints": []
14+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { Controller } from 'stimulus';
2+
3+
export default class extends Controller {
4+
connect() {
5+
console.log('app-controller');
6+
}
7+
}

fixtures/stimulus/autoimport/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
import '@symfony/mock-module/dist/style.css';
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"name": "@symfony/autoimport",
3+
"license": "MIT",
4+
"version": "1.0.0",
5+
"main": "./index.js"
6+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module.exports = {
2+
'@symfony/mock-module/mock': import(/* webpackMode: "eager" */ '@symfony/mock-module/dist/controller'),
3+
};
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"name": "@symfony/controllers",
3+
"license": "MIT",
4+
"version": "1.0.0",
5+
"main": "./index.js"
6+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { Controller } from 'stimulus';
2+
3+
export default class extends Controller {
4+
connect() {
5+
console.log('mock-module-controller');
6+
}
7+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
body {}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"name": "@symfony/mock-module",
3+
"license": "MIT",
4+
"version": "1.0.0",
5+
"symfony": {
6+
"controllers": {
7+
"mock": {
8+
"main": "dist/controller.js",
9+
"webpackMode": "eager",
10+
"enabled": true,
11+
"autoimport": {
12+
"@symfony/mock-module/dist/style.css": true
13+
}
14+
}
15+
}
16+
}
17+
}

index.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1018,6 +1018,17 @@ class Encore {
10181018
return this;
10191019
}
10201020

1021+
/**
1022+
* If enabled, the Stimulus bridge is used to load Stimulus controllers from PHP packages.
1023+
*
1024+
* @returns {Encore}
1025+
*/
1026+
enableStimulusBridge(controllerJsonPath) {
1027+
webpackConfig.enableStimulusBridge(controllerJsonPath);
1028+
1029+
return this;
1030+
}
1031+
10211032
/**
10221033
* If enabled, the react preset is added to Babel.
10231034
*

lib/WebpackConfig.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ class WebpackConfig {
105105
this.useLessLoader = false;
106106
this.useStylusLoader = false;
107107
this.useSassLoader = false;
108+
this.useStimulusBridge = false;
108109
this.useReact = false;
109110
this.usePreact = false;
110111
this.useVueLoader = false;
@@ -121,6 +122,9 @@ class WebpackConfig {
121122
resolveUrlLoader: true,
122123
resolveUrlLoaderOptions: {}
123124
};
125+
this.stimulusOptions = {
126+
controllerJsonPath: null,
127+
};
124128
this.preactOptions = {
125129
preactCompat: false
126130
};
@@ -680,6 +684,24 @@ class WebpackConfig {
680684
this.stylusLoaderOptionsCallback = stylusLoaderOptionsCallback;
681685
}
682686

687+
enableStimulusBridge(controllerJsonPath) {
688+
this.useStimulusBridge = true;
689+
690+
if (!fs.existsSync(controllerJsonPath)) {
691+
throw new Error(`File "${controllerJsonPath}" could not be found.`);
692+
}
693+
694+
// Add configured entrypoints
695+
const controllersData = JSON.parse(fs.readFileSync(controllerJsonPath));
696+
const rootDir = path.dirname(path.resolve(controllerJsonPath));
697+
698+
for (let name in controllersData.entrypoints) {
699+
this.addEntry(name, rootDir + '/' + controllersData.entrypoints[name]);
700+
}
701+
702+
this.stimulusOptions.controllersJsonPath = controllerJsonPath;
703+
}
704+
683705
enableReactPreset() {
684706
this.useReact = true;
685707
}

lib/config-generator.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ const assetOutputDisplay = require('./plugins/asset-output-display');
4040
const notifierPluginUtil = require('./plugins/notifier');
4141
const sharedEntryConcatPuginUtil = require('./plugins/shared-entry-concat');
4242
const PluginPriorities = require('./plugins/plugin-priorities');
43+
const stimulusBridge = require('./plugins/stimulus-bridge');
4344
const applyOptionsCallback = require('./utils/apply-options-callback');
4445
const sharedEntryTmpName = require('./utils/sharedEntryTmpName');
4546
const copyEntryTmpName = require('./utils/copyEntryTmpName');
@@ -457,6 +458,8 @@ class ConfigGenerator {
457458

458459
variableProviderPluginUtil(plugins, this.webpackConfig);
459460

461+
stimulusBridge(plugins, this.webpackConfig);
462+
460463
cleanPluginUtil(plugins, this.webpackConfig);
461464

462465
definePluginUtil(plugins, this.webpackConfig);

lib/plugins/stimulus-bridge.js

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*
2+
* This file is part of the Symfony Webpack Encore package.
3+
*
4+
* (c) Fabien Potencier <[email protected]>
5+
*
6+
* For the full copyright and license information, please view the LICENSE
7+
* file that was distributed with this source code.
8+
*/
9+
10+
'use strict';
11+
12+
const WebpackConfig = require('../WebpackConfig'); //eslint-disable-line no-unused-vars
13+
const createPlugin = require('@symfony/stimulus-bridge/webpack-helper');
14+
const fs = require('fs');
15+
16+
/**
17+
* @param {Array} plugins
18+
* @param {WebpackConfig} webpackConfig
19+
* @return {void}
20+
*/
21+
module.exports = function(plugins, webpackConfig) {
22+
if (webpackConfig.useStimulusBridge) {
23+
plugins.push({
24+
plugin: createPlugin(JSON.parse(fs.readFileSync(webpackConfig.stimulusOptions.controllersJsonPath))),
25+
});
26+
}
27+
};

package.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,13 +51,18 @@
5151
"webpack-dev-server": "^3.1.14",
5252
"webpack-manifest-plugin": "^2.0.2",
5353
"webpack-sources": "^1.3.0",
54+
"webpack-virtual-modules": "^0.2.2",
5455
"yargs-parser": "^18.1.3"
5556
},
5657
"devDependencies": {
5758
"@babel/plugin-proposal-class-properties": "^7.0.0",
5859
"@babel/plugin-transform-react-jsx": "^7.0.0",
5960
"@babel/preset-react": "^7.0.0",
6061
"@babel/preset-typescript": "^7.0.0",
62+
"@symfony/mock-module": "file:fixtures/stimulus/mock-module",
63+
"@symfony/controllers": "file:fixtures/stimulus/controllers",
64+
"@symfony/autoimport": "file:fixtures/stimulus/autoimport",
65+
"@symfony/stimulus-bridge": "^1.0.0",
6166
"@vue/babel-helper-vue-jsx-merge-props": "^1.0.0-beta.3",
6267
"@vue/babel-preset-jsx": "^1.0.0-beta.3",
6368
"@vue/compiler-sfc": "^3.0.0-beta.9",
@@ -87,6 +92,7 @@
8792
"sass": "^1.17.0",
8893
"sass-loader": "^9.0.1",
8994
"sinon": "^9.0.2",
95+
"stimulus": "^1.1.1",
9096
"strip-ansi": "^6.0.0",
9197
"stylus": "^0.54.5",
9298
"stylus-loader": "^3.0.2",

test/functional.js

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2072,6 +2072,34 @@ module.exports = {
20722072
});
20732073
});
20742074

2075+
it('Symfony - Stimulus standard app is built correctly', () => {
2076+
const appDir = testSetup.createTestAppDir();
2077+
2078+
const config = testSetup.createWebpackConfig(appDir, 'www/build', 'dev');
2079+
config.enableSingleRuntimeChunk();
2080+
config.setPublicPath('/build');
2081+
config.addEntry('main', './stimulus/assets/app.js');
2082+
config.enableStimulusBridge(__dirname + '/../fixtures/stimulus/assets/controllers.json');
2083+
config.configureBabel(function(config) {
2084+
config.plugins.push('@babel/plugin-proposal-class-properties');
2085+
});
2086+
2087+
testSetup.runWebpack(config, (webpackAssert) => {
2088+
expect(config.outputPath).to.be.a.directory().with.deep.files([
2089+
'main.js',
2090+
'main.css',
2091+
'manifest.json',
2092+
'entrypoints.json',
2093+
'runtime.js',
2094+
]);
2095+
2096+
// test controllers and style are shipped
2097+
webpackAssert.assertOutputFileContains('main.js', 'app-controller');
2098+
webpackAssert.assertOutputFileContains('main.js', 'mock-module-controller');
2099+
webpackAssert.assertOutputFileContains('main.css', 'body {}');
2100+
});
2101+
});
2102+
20752103
describe('copyFiles() allows to copy files and folders', () => {
20762104
it('Single file copy', (done) => {
20772105
const config = createWebpackConfig('www/build', 'production');

0 commit comments

Comments
 (0)