Initial commit from mattermost-plugin-starter-template

This commit is contained in:
Felipe M 2025-07-30 13:12:52 +02:00
commit acbc69f7eb
No known key found for this signature in database
GPG key ID: 52E5D65FCF99808A
57 changed files with 27772 additions and 0 deletions

4
webapp/.eslintignore Normal file
View file

@ -0,0 +1,4 @@
node_modules
webpack.config.js
dist
src/manifest.ts

6
webapp/.eslintrc.json Normal file
View file

@ -0,0 +1,6 @@
{
"root": true,
"extends": [
"plugin:@mattermost/react"
]
}

3
webapp/.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
.eslintcache
junit.xml
node_modules

1
webapp/.npmrc Normal file
View file

@ -0,0 +1 @@
save-exact=true

42
webapp/babel.config.js Normal file
View file

@ -0,0 +1,42 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
const config = {
presets: [
['@babel/preset-env', {
targets: {
chrome: 66,
firefox: 60,
edge: 42,
safari: 12,
},
modules: false,
corejs: 3,
debug: false,
useBuiltIns: 'usage',
shippedProposals: true,
}],
['@babel/preset-react', {
useBuiltIns: true,
}],
['@babel/typescript', {
allExtensions: true,
isTSX: true,
}],
['@emotion/babel-preset-css-prop'],
],
plugins: [
'babel-plugin-typescript-to-proptypes',
],
};
// Jest needs module transformation
config.env = {
test: {
presets: config.presets,
plugins: config.plugins,
},
};
config.env.test.presets[0][1].modules = 'auto';
module.exports = config;

1
webapp/i18n/en.json Normal file
View file

@ -0,0 +1 @@
{}

23572
webapp/package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

119
webapp/package.json Normal file
View file

@ -0,0 +1,119 @@
{
"private": true,
"scripts": {
"build": "webpack --mode=production",
"build:watch": "webpack --mode=production --watch",
"debug": "webpack --mode=none",
"debug:watch": "webpack --mode=development --watch",
"lint": "eslint --ignore-pattern node_modules --ignore-pattern dist --ext=.js,.jsx,tsx,ts . --quiet --cache",
"fix": "eslint --ignore-pattern node_modules --ignore-pattern dist --ext=.js,.jsx,tsx,ts . --quiet --fix --cache",
"test": "jest --forceExit --detectOpenHandles --verbose",
"test:watch": "jest --watch",
"test-ci": "jest --forceExit --detectOpenHandles --maxWorkers=2",
"check-types": "tsc"
},
"devDependencies": {
"@babel/cli": "7.25.6",
"@babel/core": "7.25.2",
"@babel/preset-env": "7.25.4",
"@babel/preset-react": "7.24.7",
"@babel/preset-typescript": "7.24.7",
"@babel/runtime": "7.25.6",
"@emotion/babel-preset-css-prop": "11.2.0",
"@emotion/core": "10.3.1",
"@emotion/react": "11.9.0",
"@mattermost/client": "10.8.0",
"@mattermost/eslint-plugin": "1.1.0-0",
"@mattermost/types": "10.8.0",
"@testing-library/jest-dom": "5.16.1",
"@types/babel__core": "7.20.5",
"@types/babel__template": "7.4.4",
"@types/enzyme": "3.10.11",
"@types/jest": "27.4.0",
"@types/node": "20.17.6",
"@types/react": "17.0.83",
"@types/react-dom": "17.0.11",
"@types/react-redux": "7.1.22",
"@types/react-router-dom": "5.1.5",
"@types/react-transition-group": "4.4.0",
"@typescript-eslint/eslint-plugin": "7.18.0",
"@typescript-eslint/parser": "7.0.2",
"babel-loader": "9.1.2",
"babel-plugin-formatjs": "10.3.7",
"babel-plugin-typescript-to-proptypes": "2.0.0",
"css-loader": "6.5.1",
"enzyme": "3.11.0",
"enzyme-adapter-react-17-updated": "1.0.2",
"enzyme-to-json": "3.6.2",
"eslint-plugin-import": "2.31.0",
"eslint-plugin-react": "7.34.1",
"eslint-plugin-react-hooks": "4.6.0",
"file-loader": "6.2.0",
"identity-obj-proxy": "3.0.0",
"isomorphic-fetch": "3.0.0",
"jest": "27.4.7",
"jest-canvas-mock": "2.3.1",
"jest-junit": "13.0.0",
"sass": "1.52.3",
"sass-loader": "13.0.0",
"style-loader": "3.3.1",
"webpack": "5.95.0",
"webpack-cli": "5.1.4"
},
"dependencies": {
"core-js": "3.26.0",
"mattermost-redux": "10.8.0",
"react": "17.0.2",
"react-redux": "8.0.2",
"redux": "4.2.0",
"typescript": "4.9.5"
},
"overrides": {
"enzyme-adapter-utils": {
"react": "17.0.2",
"react-dom": "17.0.2"
}
},
"jest": {
"snapshotSerializers": [
"<rootDir>/node_modules/enzyme-to-json/serializer"
],
"testPathIgnorePatterns": [
"/node_modules/",
"/non_npm_dependencies/"
],
"clearMocks": true,
"collectCoverageFrom": [
"src/**/*.{js,jsx}"
],
"coverageReporters": [
"lcov",
"text-summary"
],
"moduleNameMapper": {
"^.+\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "identity-obj-proxy",
"^.+\\.(css|less|scss)$": "identity-obj-proxy",
"^.*i18n.*\\.(json)$": "<rootDir>/tests/i18n_mock.json",
"^bundle-loader\\?lazy\\!(.*)$": "$1"
},
"moduleDirectories": [
"",
"node_modules",
"non_npm_dependencies"
],
"reporters": [
"default",
"jest-junit"
],
"transformIgnorePatterns": [
"node_modules/(?!react-native|react-router|mattermost-webapp)"
],
"setupFiles": [
"jest-canvas-mock"
],
"setupFilesAfterEnv": [
"<rootDir>/tests/setup.tsx"
],
"testURL": "http://localhost:8065"
}
}

24
webapp/src/index.tsx Normal file
View file

@ -0,0 +1,24 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import type {Store, Action} from 'redux';
import type {GlobalState} from '@mattermost/types/store';
import manifest from '@/manifest';
import type {PluginRegistry} from '@/types/mattermost-webapp';
export default class Plugin {
// eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-function
public async initialize(registry: PluginRegistry, store: Store<GlobalState, Action<Record<string, unknown>>>) {
// @see https://developers.mattermost.com/extend/plugins/webapp/reference/
}
}
declare global {
interface Window {
registerPlugin(pluginId: string, plugin: Plugin): void;
}
}
window.registerPlugin(manifest.id, new Plugin());

View file

@ -0,0 +1,10 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import manifest from './manifest';
test('Plugin manifest, id and version are defined', () => {
expect(manifest).toBeDefined();
expect(manifest.id).toBeDefined();
expect(manifest.version).toBeDefined();
});

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1 @@
{}

6
webapp/tests/setup.tsx Normal file
View file

@ -0,0 +1,6 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
// import '@mattermost/webapp/tests/setup';
export {};

37
webapp/tsconfig.json Normal file
View file

@ -0,0 +1,37 @@
{
"compilerOptions": {
"target": "esnext",
"baseUrl": "./",
"paths": {
"@/*": ["src/*"],
},
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"strictNullChecks": true,
"forceConsistentCasingInFileNames": true,
"module": "commonjs",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"experimentalDecorators": true,
"jsx": "react"
},
"include": [
"src",
"tests"
],
"exclude": [
"dist",
"node_modules",
"!node_modules/@types"
]
}

103
webapp/webpack.config.js Normal file
View file

@ -0,0 +1,103 @@
const exec = require('child_process').exec;
const path = require('path');
const PLUGIN_ID = require('../plugin.json').id;
const NPM_TARGET = process.env.npm_lifecycle_event; //eslint-disable-line no-process-env
const isDev = NPM_TARGET === 'debug' || NPM_TARGET === 'debug:watch';
const plugins = [];
if (NPM_TARGET === 'build:watch' || NPM_TARGET === 'debug:watch') {
plugins.push({
apply: (compiler) => {
compiler.hooks.watchRun.tap('WatchStartPlugin', () => {
// eslint-disable-next-line no-console
console.log('Change detected. Rebuilding webapp.');
});
compiler.hooks.afterEmit.tap('AfterEmitPlugin', () => {
exec('cd .. && make deploy-from-watch', (err, stdout, stderr) => {
if (stdout) {
process.stdout.write(stdout);
}
if (stderr) {
process.stderr.write(stderr);
}
});
});
},
});
}
const config = {
entry: [
'./src/index.tsx',
],
resolve: {
alias: {
'@': path.resolve(__dirname, 'src'),
},
modules: [
'src',
'node_modules',
path.resolve(__dirname),
],
extensions: ['*', '.js', '.jsx', '.ts', '.tsx'],
},
module: {
rules: [
{
test: /\.(js|jsx|ts|tsx)$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
cacheDirectory: true,
// Babel configuration is in babel.config.js because jest requires it to be there.
},
},
},
{
test: /\.(scss|css)$/,
use: [
'style-loader',
{
loader: 'css-loader',
},
{
loader: 'sass-loader',
options: {
sassOptions: {
includePaths: ['node_modules/compass-mixins/lib', 'sass'],
},
},
},
],
},
],
},
externals: {
react: 'React',
'react-dom': 'ReactDOM',
redux: 'Redux',
'react-redux': 'ReactRedux',
'prop-types': 'PropTypes',
'react-bootstrap': 'ReactBootstrap',
'react-router-dom': 'ReactRouterDom',
},
output: {
devtoolNamespace: PLUGIN_ID,
path: path.join(__dirname, '/dist'),
publicPath: '/',
filename: 'main.js',
},
mode: (isDev) ? 'eval-source-map' : 'production',
plugins,
};
if (isDev) {
Object.assign(config, {devtool: 'eval-source-map'});
}
module.exports = config;