Hi, I'm migrating plugins to Jira 10.3.0 using React 18 and bundling via Webpack 5 with atlassian-webresource-webpack-plugin
.
I have a plugin with the following setup:
Frontend entry: my-plugin
(entry in webpack.config.js
)
const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const WrmPlugin = require('atlassian-webresource-webpack-plugin');
module.exports = {
entry: {
'my-plugin': './src/index.tsx',
},
output: {
path: path.resolve(__dirname, '../backend/src/main/resources'),
filename: 'js/[name].js',
library: '[name]',
libraryTarget: 'amd',
publicPath: '',
},
resolve: {
extensions: ['.tsx', '.ts', '.js'],
},
module: {
rules: [
{
test: /\.(ts|tsx)$/,
use: {
loader: 'babel-loader',
options: {
presets: [
'@babel/preset-env',
'@babel/preset-typescript',
'@babel/preset-react',
],
},
},
exclude: /node_modules/,
},
{
test: /\.css$/i,
use: [MiniCssExtractPlugin.loader, 'css-loader'],
},
],
},
externals: {
react: 'jira/api/react-18',
'react-dom': 'jira/api/react-dom-18',
},
optimization: {
minimize: false,
runtimeChunk: 'single'
},
plugins: [
new WrmPlugin({
pluginKey: 'com.example.my-jira-plugin-backend',
xmlDescriptors: path.resolve(
__dirname,
'../backend/src/main/resources/META-INF/plugin-descriptors/wr-defs.xml'
),
webResourceKey: 'entrypoint-my-plugin',
providedDependencies: {
react: {
dependency: 'com.atlassian.plugins.jira-frontend-api:react-18',
import: { amd: 'jira/api/react-18', var: 'React' },
},
'react-dom': {
dependency: 'com.atlassian.plugins.jira-frontend-api:react-dom-18',
import: { amd: 'jira/api/react-dom-18', var: 'ReactDOM' },
},
},
}),
new MiniCssExtractPlugin({
filename: 'css/[name].css',
}),
],
};
Generated resources: js/my-plugin.js
, css/my-plugin.css
(visible inside the JAR under /js
and /css
)
Generated WR descriptor: wr-defs.xml
is correctly placed under META-INF/plugin-descriptors/
this is how it's generated:
I don't understand where they came from split_my-plugin and transformation extension="soy"
<bundles>
<web-resource key="split_my-plugin">
<transformation extension="js">
<transformer key="jsI18n"/>
</transformation>
<dependency>com.atlassian.plugins.atlassian-plugins-webresource-plugin:context-path</dependency>
<resource type="download" name="css/my-plugin.css" location="css/my-plugin.css"/>
<resource type="download" name="js/my-plugin.js" location="js/my-plugin.js"/>
</web-resource>
<web-resource key="entrypoint-my-plugin">
<transformation extension="js">
<transformer key="jsI18n"/>
</transformation>
<transformation extension="soy">
<transformer key="soyTransformer"/>
<transformer key="jsI18n"/>
</transformation>
<dependency>com.example.my-jira-plugin-backend:common-runtime</dependency>
<dependency>com.atlassian.plugins.atlassian-plugins-webresource-plugin:context-path</dependency>
<dependency>com.example.my-jira-plugin-backend:split_my-plugin</dependency>
</web-resource>
<web-resource key="common-runtime">
<transformation extension="js">
<transformer key="jsI18n"/>
</transformation>
<dependency>com.atlassian.plugins.atlassian-plugins-webresource-plugin:context-path</dependency>
<resource type="download" name="js/runtime.js" location="js/runtime.js"/>
</web-resource>
<web-resource key="assets-25934921-12b3-4c8c-934a-eb64dcc584cc">
<transformation extension="js">
<transformer key="jsI18n"/>
</transformation>
<transformation extension="soy">
<transformer key="soyTransformer"/>
<transformer key="jsI18n"/>
</transformation>
</web-resource>
</bundles>
Servlet page renders a basic HTML that includes:
package com.example.plugin;
import com.atlassian.plugin.spring.scanner.annotation.imports.ComponentImport;
import com.atlassian.webresource.api.UrlMode;
import com.atlassian.webresource.api.WebResourceManager;
import javax.inject.Inject;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class MyReactPageServlet extends HttpServlet {
private final WebResourceManager webResourceManager;
@Inject
public MyReactPageServlet(@ComponentImport WebResourceManager webResourceManager) {
this.webResourceManager = webResourceManager;
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
resp.setContentType("text/html;charset=utf-8");
webResourceManager.requireResource("com.example.my-jira-plugin-backend:entrypoint-my-plugin");
String html = """
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>My React Page</title>
%s
</head>
<body>
<div id="my-plugin-container"></div>
<script>
WRM.require('wr!com.example.my-jira-plugin-backend:entrypoint-my-plugin').then(function() {
require(['my-plugin'], function(mod) {
if (mod?.default) {
mod.default();
} else {
console.error('No default export in module');
}
});
}).catch(function(err) {
console.error('Failed to load web resources:', err);
});
</script>
</body>
</html>
""".formatted(webResourceManager.getRequiredResources(UrlMode.RELATIVE));
resp.getWriter().write(html);
}
}
In my atlasian-plugin.xml i connected my generated wr-defs.xml
<?xml version="1.0" encoding="UTF-8"?>
<atlassian-plugin key="com.example.my-jira-plugin-backend"
name="My Plugin"
plugins-version="2">
<plugin-info>
<description>My plugin description</description>
<version>1.0.0</version>
<vendor name="Example Company" url="https://example.com"/>
</plugin-info>
<web-resource-import key="wr-defs" file="META-INF/plugin-descriptors/wr-defs.xml"/>
<servlet name="My React Page Servlet"
key="my-react-page"
class="com.example.plugin.MyReactPageServlet">
<url-pattern>/my-react-page</url-pattern>
<load-on-startup>1</load-on-startup>
</servlet>
<rest key="my-rest" path="/myplugin" version="1.0">
<description>Provides REST endpoints for the plugin</description>
<package>com.example.plugin.api</package>
</rest>
</atlassian-plugin>
The assembly and installation of the plugin is successful, the only thing that confuses me is that there is a disabled mark opposite the wr-defs.
as a result, when I try to call my servlet, I get a 200 response from it and an error in the console:
Uncaught Error: undefined missing entrypoint-my-plugin.
Nothing else is downloaded in the network.
I've been struggling with the solution for several days now and would like to understand whether I'm thinking in the right direction or whether there is some other approach to writing jira 10 + react plugins without velosity and ajs.
And finally my pom file:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.example</groupId>
<artifactId>my-jira-plugin</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<artifactId>my-jira-plugin-backend</artifactId>
<packaging>atlassian-plugin</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<jira.version>10.3.0</jira.version>
<platform.version>8.0.3</platform.version>
<amps.version>8.2.0</amps.version>
<plugin.testrunner.version>2.0.2</plugin.testrunner.version>
<atlassian.spring.scanner.version>6.0.0</atlassian.spring.scanner.version>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.atlassian.platform.dependencies</groupId>
<artifactId>platform-public-api</artifactId>
<version>${platform.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- Atlassian Dependencies -->
<dependency>
<groupId>com.atlassian.plugins</groupId>
<artifactId>atlassian-plugins-webresource-api</artifactId>
<version>8.0.0-m003</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.atlassian.soy</groupId>
<artifactId>soy-template-renderer-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.atlassian.soy</groupId>
<artifactId>soy-template-renderer-plugin-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.atlassian.audit</groupId>
<artifactId>atlassian-audit-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.atlassian.plugins.rest</groupId>
<artifactId>atlassian-rest-v2-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.ws.rs</groupId>
<artifactId>javax.ws.rs-api</artifactId>
<version>2.1.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<version>1.3.2</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.atlassian.templaterenderer</groupId>
<artifactId>atlassian-template-renderer-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.atlassian.cache</groupId>
<artifactId>atlassian-cache-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.atlassian.plugin</groupId>
<artifactId>atlassian-spring-scanner-annotation</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
<encoding>${project.build.sourceEncoding}</encoding>
<compilerArgs>
<arg>-parameters</arg>
</compilerArgs>
</configuration>
</plugin>
<plugin>
<groupId>com.atlassian.maven.plugins</groupId>
<artifactId>jira-maven-plugin</artifactId>
<version>${amps.version}</version>
<extensions>true</extensions>
<configuration>
<productVersion>${jira.version}</productVersion>
<productDataVersion>${jira.version}</productDataVersion>
<instructions>
<Atlassian-Plugin-Key>${project.groupId}.${project.artifactId}</Atlassian-Plugin-Key>
<Export-Package>
com.example.plugin.api
</Export-Package>
<Spring-Context>*</Spring-Context>
<Import-Package>*</Import-Package>
</instructions>
</configuration>
</plugin>
<plugin>
<groupId>com.atlassian.plugin</groupId>
<artifactId>atlassian-spring-scanner-maven-plugin</artifactId>
<version>${atlassian.spring.scanner.version}</version>
<executions>
<execution>
<goals>
<goal>atlassian-spring-scanner</goal>
</goals>
<phase>process-classes</phase>
</execution>
</executions>
<configuration>
<verbose>false</verbose>
</configuration>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>atlassian-public</id>
<url>https://packages.atlassian.com/maven/repository/public</url>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>atlassian-public</id>
<url>https://packages.atlassian.com/maven/repository/public</url>
</pluginRepository>
</pluginRepositories>
</project>
HI @DmSidorov
Welcome to the community.
Best is to ask this question in the Atlassian Developer Community
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.