# SharpRatings
Learning project for Svelte and Asp.Net Core powered by F#.
Svelte client needs to be built before building asp.net app. All client code is in client
folder and build result will
be located in wwwroot
folder where asp.net is serving it. To manually build client:
cd Web/client
npm install
npm run build
After building client build web app and run it:
cd Web
dotnet run
Rollup supports watch mode which can be enabled by running dev
npm script. Every time client files change rollup
builds client again.
cd Web/client
npm run dev
Asp.net can also be run in watch mode which allows to monitor changes in wwwroot files by default.
cd Web
dotnet watch
Now it's possible to a certain limit, to edit client and server code without restarting server.
Currently using traditional MVC to render views and ApiController for api. Looks like dotnet-aspnet-codegenerator
is
not supported for F# so code scaffolding does not work.
To make building client and server easier it's possible to build client in separate build task in ``Wen.fsproj` file. Note that this builds client every time server needs to be build and can make build times longer. It's better to build client and server separately from each other which makes running them in watch mode easier.
<Target Name="Rollup" BeforeTargets="Build">
<Exec Command="npm run build" WorkingDirectory="client" ConsoleToMSBuild="true" />
</Target>
Also automatically client files as part of the project. This does not work so well in Rider because file order matters in F# projects so it would probably be easier to have client code separated from .Net project.
<ItemGroup Label="Client">
<Content Include="client\**\*.js" />
<Content Include="client\**\*.svelte" />
<Content Remove="client\node_modules\**" />
</ItemGroup>
Rollup is used for transpiling and bundling client side code into js file which is server by Asp.Net.
npm install --save-dev svelte rollup rollup-plugin-svelte @rollup/plugin-commonjs @rollup/plugin-node-resolve
Create rollup configuration rollup.config.js``: Build output is going to Asp.Net static wwwroot folder where we are serving bundled js.
plugin-node-resolveand
plugin-commonjs` are used to bundle 3rd party libraries and modules which might be in ES6 or
CommonJS syntax.
import svelte from 'rollup-plugin-svelte';
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
export default {
input: 'src/main.js',
output: {
file: '../wwwroot/js/bundle.js',
format: 'iife',
name: 'app',
},
plugins: [
commonjs(),
resolve({ browser: true }),
svelte({
emitCss: false,
compilerOptions: {
customElement: false,
},
}),
],
};
:warning: Make sure to add module resolver plugins before svelte plugin, otherwise importing dependencies won't work in Svelte components.
:warning: Svelte plugins
include
option is explicit, if you set it and do not also addnode_modules
folder then imported .svelte components are not transpiled.
Jest test can be run rollup-jest
rollup plugin.
npm install --save-dev jest rollup-jest
Using preset configuration for jest in package.json
{
"jest": {
"preset": "rollup-jest"
}
}
Svelte components needs to be precompiled for jest tests with svelte-jester
jest plugin
npm install --save-dev svelte-jester
Configure jest to preprocess svelte components
{
"jest": {
"transform": {
"^.+\\.svelte$": "svelte-jester"
},
"moduleFileExtensions": [
"js",
"svelte"
]
}
}
Svelte components can be tested with testing-library
svelte extension
npm install --save-dev @testing-library/svelte
Also add Jest DOM matchers which help in writing clean tests
npm install --save-dev @testing-library/jest-dom
Configure jest to use dom matchers
{
"setupFilesAfterEnv": [
"@testing-library/jest-dom/extend-expect"
]
}
For some reason Jest was not able to mock dependencies in Svelte components when it was transpiled with rollup-jest plugin. Mocks in normal ES6 modules worked fine. This might had something to do with how jest.mock calls needs to reorder imports or something.
Solution to the problem was to switch from jest-rollup
to jest-babel
.
npm install --save-dev @babel/core @babel/preset-env @babel/plugin-transform-runtime
Babel 7 needs also plugin-transform-runtime
to avoid ReferenceError: regeneratorRuntime is not defined
error.
Add following babel configuration .babelrc babel.json.js
with env preset:
module.exports = {
presets: ["@babel/preset-env"],
plugins: ["@babel/plugin-transform-runtime"]
};
:warning:
.babelrc
config file somehow breaks JeststransformIgnorePatterns
configuration !!! Usebabel.config.js
instead. https://github.com/svelteness/svelte-jester/issues/4
Configure Jest to use babel-jest
when transforming js files:
{
"transform": {
"^.+\\.svelte$": "svelte-jester",
"^.+\\.jsx?$": "babel-jest"
}
}
Use jest-fetch-mock to automatically mock fetch function.
npm install --save-dev jest-fetch-mock
By default jest-fetch-mock
enables mocks which needs to be reset after each test. Currently jest-fetch-mocks
readme
advices to disable automatic mock reset from jest and explicitly reset mocks after each tests run. This can lead
problems when mocks share state across tests, when resetting mocks has been forgotten from individual test and also
extra code.
It's better to make jest reset mocks after each test.
Fetch mock needs to be initialized before loading jest environment, in jest.setup.js
import fetchMock from "jest-fetch-mock";
fetchMock.enableMocks();
To allow jest-fetch-mock
to work with automatic mock resetting, it needs to be enabled before each test. This can be
done in jest after env setup file: jest.setupAfterEnv.js
import fetchMock from "jest-fetch-mock";
beforeEach(() => {
fetchMock.doMock()
})
And configure jest to use global setup file
{
"jest": {
"automock": false,
"resetMocks": true,
"setupFiles": [
"./jest.setup.js"
],
"setupFilesAfterEnv": [
"./jest.setupAfterEnv.js"
]
}
}
When testing user interaction with form components, there is much more happening than just firing simple click events. Using 'user-event' library makes firing user interaction events simpler.
npm install --save-dev @testing-library/user-event
For static code analysis install ESLint and Prettier for formatting. Configure ESLint to fail if code is not formatted correctly with prettier.
npm install --save-dev prettier eslint eslint-config-prettier eslint-plugin-prettier
Create ESLint configuration file according to your project. Styles can be ignored because we are using prettier for that.
{
"env": {
"browser": true,
"es2021": true
},
"extends": "prettier",
"plugins": [
"eslint:recommended",
"prettier"
],
"parserOptions": {
"ecmaVersion": "latest",
"sourceType": "module"
},
"rules": {
"prettier/prettier": [
"error"
]
}
}
Running eslint:
npx eslint .
Running prettier:
npx prettier -c .
Automatically formatting code with prettier:
npx prettier -fix .
Prettier forced LF line endings in 2.0 version, which will cause problems if running with git core.autocrlf=true
setting on Windows OS. Every time checking out code git will convert LF to CRLF and fail linter and forcing line endings
to LF can cause other problems in Windows, so best to allow them locally. Make sure you still commit LF line endings!
We also prefer single quote string literals in js, personal opinion. Create following Prettier configuration
file .prettierrc.json
{
"endOfLine": "auto",
"singleQuote": true
}
For linting test files add eslint-plugin-jest
plugin.
npm install --save-dev eslint-plugin-jest
Configure recommended rules or all rules.
{
"extends": [
"plugin:jest/recommended"
],
"plugins": [
"jest"
]
}
Add following plugins to enforce good testing library use.
npm install -D eslint-plugin-testing-library eslint-plugin-jest-dom
Configure ESLint:
{
"extends": [
"plugin:testing-library/dom"
],
"plugins": [
"testing-library",
"jest-dom"
]
}
Add following plugin for linting Svelte components.
npm install --save-dev eslint-plugin-svelte3
Configure ESLint Svelte plugin and add processing override for Svelte component files.
{
"plugins": [
"svelte3"
],
"overrides": [
{
"files": [
"**/*.svelte"
],
"processor": "svelte3/svelte3"
}
]
}
Include Svelte component file extension when running linter.
eslint . --ext .js,.svelte
:warning:
prettier-plugin-svelte
does not work witheslint-prettier-plugin
!!! It is better to keep ESLint separated from prettier and usenpx prettier . -c
command to check if there is formatting errors.
Add following plugin to Prettier for formatting Svelte components.
npm install --save-dev prettier-plugin-svelte
Configure file override for Svelte components.
{
"plugins": [
"svelte3"
],
"overrides": [
{
"files": [
"*.svelte"
],
"processor": "svelte3/svelte3"
}
]
}
Add .svelte component extension when running ESLint.
eslint . --ext .js,.svelte
Svelte-form-lib provides nice abstraction for handling form state and it integrates with yup validation library.
npm install --save svelte-forms-lib yup
Svelte Material UI is Svelte implementation of Googles CSS framework. SMUI library components are split in separate npm packages to decrease project bundle size.
Each component needs to be individually added into project. eg.
npm install --save-dev @smui/button
npm install --save-dev @smui/top-app-bar
...
Material UI comes with them support and separate theme builder.
npm install --save smui-theme
Create default theme from template.
npx smui-theme template src/theme
This theme needs to be compiled every time component is added or removed, so good place to add is as prepare
npm
script.
{
"scripts": {
"dev": "rollup -c -w",
"prepare": "smui-theme compile ../wwwroot/smui.css -i src/theme"
}
}
Because Svelte Material UI components needs to be transpiled with other code Jest needs to be configured to also include
those modules. By default Jest does not transpile modules under node_modules
.
Add following to jest.config.js
transformIgnorePatterns: ['node_modules/(?!(@smui|@material))']