How does webpack-encore works with symfony 5? - javascript

I'm getting a bit mad dealing with webpack-encore in a symfony 5 project.
There is few things i just don't understand. first of all here is my webpack.config.js :
const Encore = require('#symfony/webpack-encore');
// Manually configure the runtime environment if not already configured yet by the "encore"
command.
// It's useful when you use tools that rely on webpack.config.js file.
if (!Encore.isRuntimeEnvironmentConfigured()) {
Encore.configureRuntimeEnvironment(process.env.NODE_ENV || 'dev');
}
Encore
// directory where compiled assets will be stored
.setOutputPath('public/build/')
// public path used by the web server to access the output path
.setPublicPath('/build')
// only needed for CDN's or sub-directory deploy
//.setManifestKeyPrefix('build/')
/*
* ENTRY CONFIG
*
* Each entry will result in one JavaScript file (e.g. app.js)
* and one CSS file (e.g. app.css) if your JavaScript imports CSS.
*/
.addEntry('app', './assets/app.js')
// enables the Symfony UX Stimulus bridge (used in assets/bootstrap.js)
.enableStimulusBridge('./assets/controllers.json')
// When enabled, Webpack "splits" your files into smaller pieces for greater optimization.
.splitEntryChunks()
// will require an extra script tag for runtime.js
// but, you probably want this, unless you're building a single-page app
.enableSingleRuntimeChunk()
/*
* FEATURE CONFIG
*
* Enable & configure other features below. For a full
* list of features, see:
* https://symfony.com/doc/current/frontend.html#adding-more-features
*/
.cleanupOutputBeforeBuild()
.enableBuildNotifications()
.enableSourceMaps(!Encore.isProduction())
// enables hashed filenames (e.g. app.abc123.css)
.enableVersioning(Encore.isProduction())
.configureBabel((config) => {
config.plugins.push('#babel/plugin-proposal-class-properties');
})
// enables #babel/preset-env polyfills
.configureBabelPresetEnv((config) => {
config.useBuiltIns = 'usage';
config.corejs = 3;
})
// enables Sass/SCSS support
//.enableSassLoader()
// uncomment if you use TypeScript
//.enableTypeScriptLoader()
// uncomment if you use React
//.enableReactPreset()
// uncomment to get integrity="..." attributes on your script & link tags
// requires WebpackEncoreBundle 1.4 or higher
//.enableIntegrityHashes(Encore.isProduction())
// uncomment if you're having problems with a jQuery plugin
//.autoProvidejQuery();
module.exports = Encore.getWebpackConfig();
The thing is when i use {{ asset('build/images/my-image.png') }} in my template the file is not found though it is in assets/images/my-image.png
How should i access my image???
Why it is not in manifest.json ??
Why am i not having images in my public/build/ folder ?
What path should i be using to reference my image in app.css as a background-image for example ?
This thing is a nightmare to use & configure.....
Thanks in advance

Thank you guys you are both right,
.copyFiles({
from: './assets/images',
// optional target path, relative to the output dir
to: 'images/[path][name].[ext]',
// if versioning is enabled, add the file hash too
//to: 'images/[path][name].[hash:8].[ext]',
// only copy files matching this pattern
//pattern: /\.(png|jpg|jpeg)$/
})
this part was missing in my config files, then i did not run the build command.....
But it still not an easy tool.
Have a good day.

you tryed run command for build files npm run build ?
or run command for recompile automatically assets
how of documentation exemple
https://symfony.com/doc/current/frontend/encore/simple-example.html

Related

Vue CLI build target lib -> externalize images (Vue 3)

I have a question regarding vue-cli's target build lib. I have an app that produces a custom element following this documentation. Check:
/* ce.js */
import { defineCustomElement } from 'vue';
import Chat from './App.ce.vue';
const element = defineCustomElement(Chat);
customElements.define('chat-widget', element);
The build command looks as follows:
/* package.json */
"build-wc": "vue-cli-service build --target lib --inline-vue --name chat-widget --dest ./wwwroot src/ce.js"
This is actually working all fine but not exactly how i want it. My images are all generated inline which totally bloats my generated umd file. Also when i put my app on a server it refuses to load the images when inline because of Content-Security-Policy issues (another discussion).
Is there a way to tell webpack / vue-cli that I want my images in separate folder? Preferably in the destination folder under /img.
Figured it out! Just set the maxSize of the image parser to very low and the images will be dropped in /img in the output folder. Cheers!
const { defineConfig } = require("#vue/cli-service");
module.exports = defineConfig({
...
chainWebpack: (config) => {
...
config.module.rule("images").set("parser", {
dataUrlCondition: {
maxSize: 4 * 1024, // 4KiB
},
});
...
},
});

How to use node modules outside of webpack encore files?

I have started using webpack encore in my Symfony 5 web app and still haven't figured it all out.
I'd like to use node modules outside of webpack encore files but I can't find a way of doing this.
For example I have installed Datatables and I'd like to use it in a JavaScript file inside the public/assets/js directory, but when I try I get $(...).DataTable is not a function.
In my app.js I do import all the modules I need :
import 'select2';
import 'parsleyjs';
import 'datatables';
Here is my webpack.config.js :
const Encore = require('#symfony/webpack-encore');
// Manually configure the runtime environment if not already configured yet by the "encore" command.
// It's useful when you use tools that rely on webpack.config.js file.
if (!Encore.isRuntimeEnvironmentConfigured()) {
Encore.configureRuntimeEnvironment(process.env.NODE_ENV || 'dev');
}
Encore
// directory where compiled assets will be stored
.setOutputPath('public/build/')
// public path used by the web server to access the output path
.setPublicPath('/build')
// only needed for CDN's or sub-directory deploy
//.setManifestKeyPrefix('build/')
/*
* ENTRY CONFIG
*
* Each entry will result in one JavaScript file (e.g. app.js)
* and one CSS file (e.g. app.css) if your JavaScript imports CSS.
*/
.addEntry('app', './assets/app.js')
// enables the Symfony UX Stimulus bridge (used in assets/bootstrap.js)
.enableStimulusBridge('./assets/controllers.json')
// When enabled, Webpack "splits" your files into smaller pieces for greater optimization.
.splitEntryChunks()
// will require an extra script tag for runtime.js
// but, you probably want this, unless you're building a single-page app
// .enableSingleRuntimeChunk()
.disableSingleRuntimeChunk()
/*
* FEATURE CONFIG
*
* Enable & configure other features below. For a full
* list of features, see:
* https://symfony.com/doc/current/frontend.html#adding-more-features
*/
.cleanupOutputBeforeBuild()
.enableBuildNotifications()
.enableSourceMaps(!Encore.isProduction())
// enables hashed filenames (e.g. app.abc123.css)
.enableVersioning(Encore.isProduction())
.configureBabel((config) => {
config.plugins.push('#babel/plugin-proposal-class-properties');
})
// enables #babel/preset-env polyfills
.configureBabelPresetEnv((config) => {
config.useBuiltIns = 'usage';
config.corejs = 3;
})
// enables Sass/SCSS support
//.enableSassLoader()
// uncomment if you use TypeScript
//.enableTypeScriptLoader()
// uncomment if you use React
//.enableReactPreset()
// uncomment to get integrity="..." attributes on your script & link tags
// requires WebpackEncoreBundle 1.4 or higher
//.enableIntegrityHashes(Encore.isProduction())
// uncomment if you're having problems with a jQuery plugin
.autoProvidejQuery()
;
module.exports = Encore.getWebpackConfig();

How to use a glob pattern in the scripts section of angular.json?

I have a hybrid AngularJS/Angular application that will take some time to complete migration to fully be an Angular app. While this process occurs, I'd like to move away from the previous build system to using the CLI and webpack to manage all of the old AngularJS scripts as well. This is possible as I've done it before by adding all of my scripts to the scripts section in angular.json like the following:
"scripts": [
"src/app/angularjs/app.js",
"src/app/angularjs/controllers/main.js",
"src/app/angularjs/services/someService.js",
"src/app/angularjs/controllers/someController.js"
],
This works well and the CLI builds via ng serve and ng build continue to work for the hybrid bootstrapped app as needed. The problem I'm running into now is manually listing each file for the current application I'm migrating is not ideal. I have hundreds of scripts that need to be added, and what I need is to be able to use a globbing pattern like the following:
"scripts": [
"src/app/angularjs/**/*.js"
],
The problem is this syntax from what I can tell is not supported. The glob pattern is supported in the assets section of angular.json as stated here but not in the scripts section: https://angular.io/guide/workspace-config#assets-configuration
In the scripts section I can't find a similar solution. It does have an expanded object API, but nothing that solves the problem I can tell to select all .js files from a particular directory as listed here: https://angular.io/guide/workspace-config#styles-and-scripts-configuration
Is it possible by some means to use a glob pattern or similar approach to select all files of a directory for the scripts section in angular.json so I don't have to manually list out hundreds of individual .js files?
The Bad News
The scripts section does not support the same glob patterns that the assets section does.
The Good News(?)
Since you're transitioning away from AngularJS, you hopefully won't have any new files to import in the future, so you could just generate the list of all the files you need to import.
Make your way to the src/app/angular directory and run the following:
find . -iregex '.*\.\(js\)' -printf '"%p",\n'
That will give you your list, already quoted for your convenience. You may need to do a quick search/replace (changing "." to "src/app/angularjs"), and don't forget to remove the last comma, but once you've done that once you should be all set.
The Extra News
You can further filter out unwanted files with -not, so (per your comment) you might do:
find . -iregex '^.*\.js$' -not -iregex '^.*_test\.js$' -printf '"%p",\n'
And that should give you all your .js files without your _test.js files.
KISS
Of course, this isn't a complex pattern, so as #atconway points out below, this will work just as well:
find . -iname "*.js" -not -iname "*_test.js" -printf '"%p",\n'
I'll keep the above, though, for use in situations where the full power of regex might come in handy.
I wanted to extend an anser of #JasonVerber and here is a Node.JS code and therefore (I believe) cross-platform.
Firstly install find package and then save contents from the snippet in some file.js.
Afterwards, specify paths so that they resolve to where you wan't to get your files from and where to put the resulting file to.
After that node file-name.js and this will save all found file paths to the resultPath in result.txt ready to Ctrl+A, Ctrl+C, Ctrl+V.
const find = require('find');
const path = require('path');
const fs = require('fs');
// BEFORE USAGE INSTALL `find` package
// Path to the folder where to look for files
const sourcePath = path.resolve(path.join(__dirname, 'cordova-app', 'src'));
// Path that will be removed from absolute path to files
const pathToRemove = path.resolve(path.join(__dirname, 'cordova-app'));
// Path where to put result.txt
const resultPath = path.resolve(path.join(__dirname, './result.txt'));
// Collects the file paths
const res = [];
// Path with replaced \ onto /
const pathToRemovehReplaced = pathToRemove.replace(/\\/g, '/');
// Get all fils that match a regex
find.eachfile(/\.js$/, sourcePath, file => {
// First remove all \ with / and then remove the path from root to source so that only relative path is left
const fileReplaced = file.replace(/\\/g, '/').replace(`${pathToRemovehReplaced}/`, '');
// Surround with quoutes
res.push(`"${fileReplaced}"`);
}).end(() => {
// Write file and concatenate results with newline and commas
fs.writeFileSync(resultPath, res.join(',\r\n'), 'utf8');
console.log('DONE!');
});
The result I got while testing (/\.ts$/ for regex)
"src/app/app.component.spec.ts",
"src/app/app.component.ts",
"src/app/app.module.ts",
"src/environments/environment.prod.ts",
"src/environments/environment.ts",
"src/main.ts",
"src/polyfills.ts",
"src/test.ts"

Compiling dynamically required modules with Browserify

I am using Browserify to compile a large Node.js application into a single file (using options --bare and --ignore-missing [to avoid troubles with lib-cov in Express]). I have some code to dynamically load modules based on what is available in a directory:
var fs = require('fs'),
path = require('path');
fs.readdirSync(__dirname).forEach(function (file) {
if (file !== 'index.js' && fs.statSync(path.join(__dirname, file)).isFile()) {
module.exports[file.substring(0, file.length-3)] = require(path.join(__dirname, file));
}
});
I'm getting strange errors in my application where aribtrary text files are being loaded from the directory my compiled file is loaded in. I think it's because paths are no longer set correctly, and because Browserify won't be able to require() the correct files that are dynamically loaded like this.
Short of making a static index.js file, is there a preferred method of dynamically requiring a directory of modules that is out-of-the-box compatible with Browserify?
This plugin allows to require Glob patterns: require-globify
Then, with a little hack you can add all the files on compilation and not executing them:
// Hack to compile Glob files. Don´t call this function!
function ಠ_ಠ() {
require('views/**/*.js', { glob: true })
}
And, for example, you could require and execute a specific file when you need it :D
var homePage = require('views/'+currentView)
Browserify does not support dynamic requires - see GH issue 377.
The only method for dynamically requiring a directory I am aware of: a build step to list the directory files and write the "static" index.js file.
There's also the bulkify transform, as documented here:
https://github.com/chrisdavies/tech-thoughts/blob/master/browserify-include-directory.md
Basically, you can do this in your app.js or whatever:
var bulk = require('bulk-require');
// Require all of the scripts in the controllers directory
bulk(__dirname, ['controllers/**/*.js']);
And my gulpfile has something like this in it:
gulp.task('js', function () {
return gulp.src('./src/js/init.js')
.pipe(browserify({
transform: ['bulkify']
}))
.pipe(rename('app.js'))
.pipe(uglify())
.pipe(gulp.dest('./dest/js'));
});

Testing with Karma and RequireJS with files in CommonJS syntax

I'm working on an angular application that is written in CommonJS syntax and uses a grunt task with the grunt-contrib-requirejs task to translate the source files to AMD format and compile it into one output file. My goal is to make Karma work with RequireJS and keep my source files and spec files in CommonJS syntax.
I've been able to get a simple test passing in AMD format with the following file structure:
-- karma-test
|-- spec
| `-- exampleSpec.js
|-- src
| `-- example.js
|-- karma.conf.js
`-- test-main.js
and the following files:
karma.conf.js
// base path, that will be used to resolve files and exclude
basePath = '';
// list of files / patterns to load in the browser
files = [
JASMINE,
JASMINE_ADAPTER,
REQUIRE,
REQUIRE_ADAPTER,
'test-main.js',
{pattern: 'src/*.js', included: false},
{pattern: 'spec/*.js', included: false}
];
// list of files to exclude
exclude = [];
// test results reporter to use
// possible values: 'dots', 'progress', 'junit'
reporters = ['progress'];
// web server port
port = 9876;
// cli runner port
runnerPort = 9100;
// enable / disable colors in the output (reporters and logs)
colors = true;
// level of logging
// possible values: LOG_DISABLE || LOG_ERROR || LOG_WARN || LOG_INFO || LOG_DEBUG
logLevel = LOG_DEBUG;
// enable / disable watching file and executing tests whenever any file changes
autoWatch = true;
// Start these browsers, currently available:
browsers = ['Chrome'];
// If browser does not capture in given timeout [ms], kill it
captureTimeout = 60000;
// Continuous Integration mode
// if true, it capture browsers, run tests and exit
singleRun = false;
example.js
define('example', function() {
var message = "Hello!";
return {
message: message
};
});
exampleSpec.js
define(['example'], function(example) {
describe("Example", function() {
it("should have a message equal to 'Hello!'", function() {
expect(example.message).toBe('Hello!');
});
});
});
test-main.js
var tests = Object.keys(window.__karma__.files).filter(function (file) {
return /Spec\.js$/.test(file);
});
requirejs.config({
// Karma serves files from '/base'
baseUrl: '/base/src',
// Translate CommonJS to AMD
cjsTranslate: true,
// ask Require.js to load these files (all our tests)
deps: tests,
// start test run, once Require.js is done
callback: window.__karma__.start
});
However, my goal is to write both the source file and the spec file in CommonJS syntax with the same results, like so:
example.js
var message = "Hello!";
module.exports = {
message: message
};
exampleSpec.js
var example = require('example');
describe("Example", function() {
it("should have a message equal to 'Hello!'", function() {
expect(example.message).toBe('Hello!');
});
});
But despite having the cjsTranslate flag set to true, I just receive this error:
Uncaught Error: Module name "example" has not been loaded yet for context: _. Use require([])
http://requirejs.org/docs/errors.html#notloaded
at http://localhost:9876/adapter/lib/require.js?1371450058000:1746
Any ideas on how this can be accomplished?
Edit: I found this issue for the karma-runner repo: https://github.com/karma-runner/karma/issues/552 and there's a few comments that may help with this problem, but I haven't had any luck with them so far.
The solution I ended up finding involved using grunt and writing some custom grunt tasks. The process goes like this:
Create a grunt task to build a bootstrap requirejs file by finding all specs using a file pattern, looping through them and building out a traditional AMD style require block and creating a temporary file with code like this:
require(['spec/example1_spec.js'
,'spec/example2_spec.js',
,'spec/example3_spec.js'
],function(a1,a2){
// this space intentionally left blank
}, "", true);
Create a RequireJS grunt task that compiles the above bootstrap file and outputs a single js file that will effectively include all source code, specs, and libraries.
requirejs: {
tests: {
options: {
baseUrl: './test',
paths: {}, // paths object for libraries
shim: {}, // shim object for non-AMD libraries
// I pulled in almond using npm
name: '../node_modules/almond/almond.min',
// This is the file we created above
include: 'tmp/require-tests',
// This is the output file that we will serve to karma
out: 'test/tmp/tests.js',
optimize: 'none',
// This translates commonjs syntax to AMD require blocks
cjsTranslate: true
}
}
}
Create a grunt task that manually starts a karma server and serve the single compiled js file that we now have for testing.
Additionally, I was able to ditch the REQUIRE_ADAPTER in the karma.conf.js file and then only include the single compiled js file instead of the patterns that matched all source code and specs, so it looks like this now:
// base path, that will be used to resolve files and exclude
basePath = '';
// list of files / patterns to load in the browser
files = [
JASMINE,
JASMINE_ADAPTER,
REQUIRE,
'tmp/tests.js'
];
// list of files to exclude
exclude = [];
// test results reporter to use
// possible values: 'dots', 'progress', 'junit'
reporters = ['progress'];
// web server port
port = 9876;
// cli runner port
runnerPort = 9100;
// enable / disable colors in the output (reporters and logs)
colors = true;
// level of logging
// possible values: LOG_DISABLE || LOG_ERROR || LOG_WARN || LOG_INFO || LOG_DEBUG
logLevel = LOG_INFO;
// enable / disable watching file and executing tests whenever any file changes
autoWatch = true;
// Start these browsers, currently available:
browsers = ['PhantomJS'];
// If browser does not capture in given timeout [ms], kill it
captureTimeout = 60000;
// Continuous Integration mode
// if true, it capture browsers, run tests and exit
singleRun = true;
In the grunt task configuration for the requirejs compilation, it was also necessary to use almond in order to start the test execution (test execution would hang without it). You can see this used in the requirejs grunt task config above.
There's a couple things. First of all: I might have missed some details in your question (as it is super huge) - so sorry about that.
In short, you may want to checkout Backbone-Boilerplate wip branch testing organization: https://github.com/backbone-boilerplate/backbone-boilerplate/tree/wip
First: RequireJS does not support unwrapped raw common.js module. cjsTranslate is a R.js (the build tool) option to convert Commonjs to AMD compatible during build. As so, requiring a CJS raw module won't work. To resolve this issue, you can use a server to filter the scripts sent and compile them to AMD format. On BBB, we pass file through a static serve to compile them:
karma proxies setting: https://github.com/backbone-boilerplate/backbone-boilerplate/blob/wip/Gruntfile.js#L232-L234
Server setting: https://github.com/backbone-boilerplate/backbone-boilerplate/blob/wip/Gruntfile.js#L173-L179
Second: The Karma requirejs plugin isn't working super well - and it's somehow easy to use requireJS directly. On BBB, that's how we managed it: https://github.com/backbone-boilerplate/backbone-boilerplate/blob/wip/test/jasmine/test-runner.js#L16-L36
Hope this helps!

Categories