1

What I need to do:

Create a shared runtime environment so that my excel custom functions can have access to the workbook information.

Current Setup: The way the project seems to have been set up is, using the yeoman office generator to generate a custom function addin and then using vue-cli to generate a vue app to use as the taskpane.

Folder Structure:

RootFolder
Add-In
    dist
    node_modules
    src
    config.json
    manifest.xml
    jest.config.js
    package.json
    tsconfig.json
    webpack.config.js
Taskpane
    dist
    public
    node_modules
    src
        assets
        modules
        plugins
        router
        store
        views
        App.vue
        main.ts
        shims-vue.d.ts
        shims.tsx.d.ts
        shims.vuetify.d.ts
    .browserlistrc
    .eslintrc.js
    babel.config.js
    package.json
    tsconfig.json
    vue.config.js

Webpack config for addin prior to changes:

  module.exports = async (env, options) => {
  const dev = options.mode === "development";
  const buildType = dev ? "dev" : "prod";
  const config = {
    node: {
      fs: 'empty' // https://github.com/webpack-contrib/css-loader/issues/447
    },
    devtool: "source-map",
    entry: {
      functions: "./src/functions/functions.ts",
      polyfill: "@babel/polyfill",
      commands: "./src/commands/commands.ts"
    },
    resolve: {
      extensions: [".ts", ".tsx", ".html", ".js"]
    },
    module: {
      rules: [
        {
          test: /\.ts$/,
          exclude: /node_modules/,
          use: "babel-loader"
        },
        {
          test: /\.tsx?$/,
          exclude: /node_modules/,
          use: "ts-loader"
        },
        {
          test: /\.html$/,
          exclude: /node_modules/,
          use: "html-loader"
        },
        {
          test: /\.(png|jpg|jpeg|gif)$/,
          loader: "file-loader",
          options: {
            name: '[path][name].[ext]',          
          }
        }
      ]
    },
    plugins: [
      new CleanWebpackPlugin({
        cleanOnceBeforeBuildPatterns: dev ? [] : ["**/*"]
      }),
      new CustomFunctionsMetadataPlugin({
        output: "functions.json",
        input: "./src/functions/functions.ts"
      }),
      new EnvironmentPlugin({
        NODE_ENV: env.NODE_ENV || 'local'
      }),
      new HtmlWebpackPlugin({
        filename: "functions.html",
        template: "./src/functions/functions.html",
        chunks: ["polyfill", "functions"]
      }),
      new CopyWebpackPlugin([
        {
          to: "[name]." + buildType + ".[ext]",
          from: "manifest*.xml",
          transform(content) {
            if (dev) {
              return content;
            } else {
              return content.toString().replace(new RegExp(urlDev, "g"), urlProd);
            }
          }
        }
      ]),
      new HtmlWebpackPlugin({
        filename: "commands.html",
        template: "./src/commands/commands.html",
        chunks: ["polyfill", "commands"]
      })
    ],
    devServer: {
      headers: {
        "Access-Control-Allow-Origin": "*"
      },      
      https: (options.https !== undefined) ? options.https : await devCerts.getHttpsServerOptions(),
      port: process.env.npm_package_config_dev_server_port || 4000
    }
  };

  return config;
};

Main.ts File:

import Vue from "vue";
import App from "./App.vue";
import router from "./router";
import store from "./store/index";
import vuetify from "./plugins/vuetify";

Vue.config.productionTip = false;

/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
(window as any).Office.onReady(() => {
  new Vue({
    router,
    store,
    vuetify,
    render: (h) => h(App),
  }).$mount("#app");
});

tsconfig.json - add-in

{
  "compilerOptions": {
    "allowJs": true,
    "baseUrl": ".",
    "esModuleInterop": true,
    "experimentalDecorators": true,
    "jsx": "react",
    "module": "commonjs",
    "noEmitOnError": true,
    "outDir": "lib",
    "resolveJsonModule": true,
    "sourceMap": true,
    "target": "es5",
    "lib": [
      "es2015",
      "dom"
    ]
  },
  "exclude": [
    "node_modules",
    "dist",
    "lib",
    "lib-amd"
  ]
}

tsconfig.json - taskpane

{
  "compilerOptions": {
    "target": "esnext",
    "module": "esnext",
    "strict": true,
    "jsx": "preserve",
    "importHelpers": true,
    "moduleResolution": "node",
    "experimentalDecorators": true,
    "resolveJsonModule": true,
    "skipLibCheck": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "sourceMap": true,
    "baseUrl": ".",
    "types": [
      "webpack-env",
      "node"
    ],
    "paths": {
      "@/*": [
        "src/*"
      ]
    },
    "lib": [
      "esnext",
      "dom",
      "dom.iterable",
      "scripthost"
    ]
  },
  "include": [
    "src/**/*.ts",
    "src/**/*.tsx",
    "src/**/*.vue",
    "tests/**/*.ts",
    "tests/**/*.tsx"
  ],
  "exclude": [
    "node_modules"
  ]
}

The Add-In folder holds the custom functions and the Taskpane folder holds the Vuejs app. I have left out other default files and added these because I believe they will help communicate my possible issue.

Steps To Merge:

I have edited Manifest.xml following the steps provided on Microsofts documentation. Updated name to use SharedRuntime

<Set Name="SharedRuntime" MinVersion="1.1"/>

Added Runtimes tags within the Host tag

<Runtimes>
    <Runtime resid="Taskpane.Url" lifetime="long" />
</Runtimes>

Updated the Page tag to use Taskpane.Url

<Page>
    <SourceLocation resid="Taskpane.Url"/>
</Page>

Updated the FunctionFile tag with Taskpane.Url

<FunctionFile resid="Taskpane.Url"/>

In webpack.config.js: I have removed the HtmlWebpackPlugin references and replaced with the following.

new HtmlWebpackPlugin({
filename: "index.html",
template: "../taskpane/public/index.html",
chunks: ["polyfill", "taskpane", "commands", "functions"]
})

Steps I did not provided in the Documentation: Since the project was created using the yeoman office generator with the customfunctions option selected, I believe I needed to add the following to the webpack.config.js file's entry object:

entry: {
    functions: "./src/functions/functions.ts",
    polyfill: "@babel/polyfill",
    commands: "./src/commands/commands.ts",
    taskpane: "../taskpane/src/main.ts"    <----- new addition

Problem: When I try to build the project it throws errors such as:

ERROR in ../taskpane/src/main.ts Module build failed Error: TypeScript emitted no output for main.ts

Module not found errors

Property doesn't exist errors

More Info:

I'm thinking (but would love to confirm) that the steps I took to create the shared runtime provided by the documentation are indeed correct, along with adding the taskpane file to the entry object in the webpack config. But I just can't figure out why it won't compile. I'm thinking it has to do with the different tsconfig.json configurations. The addin is using target: "es5" while taskpane is using target: "esnext". There are other differences as well. If somebody can help me with this and needs that information, I will be more than happy to add it.

5
  • @FreeSoftwareServers Thanks for helping look into this! I have added the rest of the folder structure, along with my webpack config prior to the additions listed in the description. Commented Jul 7, 2022 at 2:20
  • There is. I have added that to the description as well. Commented Jul 8, 2022 at 17:36
  • Sure thing, I have added tsconfig for both taskpane and add-in. Commented Jul 11, 2022 at 13:20
  • Honestly, I'm a bit out of my league, you may need to bounty, but I did see this which may help. stackoverflow.com/questions/49969071/… Commented Jul 13, 2022 at 23:34
  • 1
    Thanks for helping look into it! If I'm able to implement anything that fixes it from the link you provided, I'll be sure to report back. Commented Jul 19, 2022 at 14:18

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.