Early Adopter's guide to Umbraco v14 - Package structure.
Tags: umbraco
Published: 2024-01-27 12:42:21

If like me you have been keeping a keen eye on Umbraco things you will know that Umbraco v14 is next, and it is the version where the backoffice is being completely replaced. 

out goes the familiar if a little clunky (and unsupported) AngularJs back end and in comed the all new shiny-trendy-froody WebComponents, Typescript, Lit, platform. 

Personally i know this is going to be a steep learning curve, and i've started to look at how we are going to migrate our packages such as uSync and Translation Manager to this brave new world. 

Initially i did some open-it-up-and-just-start-migrating work, and that has given me some insights, I will probibly start again, with a few tweaks to make things easier on myself. 

Now this post, isn't about my journey its about setting up the project structures so you can build an Umbraco package and get it all working for yourself. 

**this is early adoptor advice based on Umbraco14-preview008 - its likely to change when new releases come out**

Also, I am planning to make this into a .net template which sort of means you won't have to type all of this just to get yourself a package ready project.


> ## Update!
> **There is a now a dotnet new template that will build a project just like this for you. [See our blog post on the template for more info.
](https://dev.to/kevinjump/early-adopters-umbraco-package-template-fbh)**

## Project Structure 
Now personally i like to setup my solutions so i have a project for the package and a test website so i can see what is going on. 

```
+ -- MyPackageProject.sln
  + -- <package>.csproj
  + -- <package>.Client.csproj
  + -- <package>.Core.csproj
  + -- MyPackage.Site.csproj
```

### `<package>.client.csproj`
This is the backoffice front end project, it will have all the typescript,javascript, vite stuff in it.

this project will be a razor class library, and will compile down so the assets are in a `wwwroot/app_plugins/package` folder which is the thing Umbraco will absorb.

### `<package>.core.csproj`
the core package has all the backend code/controllers/api endpoints in it, this is the bit of code that runs on the server does the work within the CMS, anything backendy.

> For more complex packages i am considering seperating out any management api controllers from this package, then i can share core logic between v13 and v14 sites and only have the v14 controllers in a seperate project. but this is only a theory right now! 

### `<package>.csproj`
this is the nuget package, usually really simple, it contains references to the .assets and core packages, so when its built this is the thing people will install. 

## Step 1: Creating the solution.
This bit is probibly the simplest, you can create a new blank solution in visual studio, 

Add three new Class Library projects - once core, assets and package class. these are the packages. 

or you can do it via the command line (hint: this can be made scriptable)

```bash
dotnet new classlib -n MyPackage
dotnet new classlib -n MyPackage.Core
dotnet new classlib -n MyPackage.Client
```

### Create an umbraco site. 
Again if you have the templates installed you can do this in visual studio. 

there is [a guide to install the latest preview releases on the Umbraco documentation site](https://docs.umbraco.com/umbraco-backoffice/getting-started/installing-preview-builds).

For the console junkie the cli commands are: 

```bash
dotnet new install Umbraco.Templates::14.0.0--preview004
dotnet new umbraco -n MyPackage.Website 
```

### Add everything to the solution.
Again in visual studio, this will have happened as you added the projects. 

```
dotnet sln add (ls -r **/*.csproj)
```

### ClassLib to RazorLib.
Now you can create razorclasslibraries directly, but you end up deleting lots of files. If you create classlibraries you can make them razor class libraries by chaning a line in the .csproj file. 

in the `.client` project. change :

```xml
<Project Sdk="Microsoft.NET.Sdk.">
```
to 
```xml
<Project Sdk="Microsoft.NET.Sdk.Razor">
```

and add a `<StaticWebAssetBasePath>` value to the property group element.
		
```xml
<StaticWebAssetBasePath>/</StaticWebAssetBasePath>
```

This tells Asp.Net that anything in the `wwwroot` folder will be presented in the website at the root level. meaning if we put our files in `app_plugins/package` then they will be accessible at https://site/app_plugins/package

you `package.client.csproj` file should look like this.

```xml
<Project Sdk="Microsoft.NET.Sdk.Razor">
  <PropertyGroup>
    <TargetFramework>net8.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
    <StaticWebAssetBasePath>/</StaticWebAssetBasePath>
  </PropertyGroup>
</Project>
```

### Dependencies.
Inorder to the packages to relate to each other when they build, we need to make certain projects have dependencies on others. 

the `package.csproj` project needs to have a dependency on the `.client` and `.core` projects - so when we build the nuget package will also have these dependencies. 

```bash
dotnet add .\MyPackage\ reference .\MyPackage.Core\
dotnet add .\MyPackage\ reference .\MyPackage.Client\
```

Also we should make it so our website has a dependency on the package, so we can test it.

```bash
dotnet add .\MyPackage\ reference .\MyPackage.Website\
```

---

Now if that has all worked, (either in visual studio or in the command line) your solution should look something like this. 

![Project structure in visual studio](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1lqo4enqs98muihm9tlq.png)

_hint : `ii ./mypackageproject.sln` will open the solution in visual studio from the command line._

## 2. Vite and all that
Now you have the basic structure down, you are almost there, any code you right in the .core project will end up in the website and any files in the wwwroot folder of the .assets project will end up visible to Umbraco.

one minor flaw you have no files in the wwwroot folder (hey you probibly don't even have a wwwroot folder).

this is because we haven't added any of the new front end magic yet. 

Here we are going to be doing close to [what the umbraco docs tell us to do](https://docs.umbraco.com/umbraco-backoffice/extending/development-flow/vite-package-setup) but with some tweaks. 

```
cd ./mypackage.assets 
npm create vite@latest assets -- --template lit-ts
```

this will create the vite package in a sub folder of your assets project. **but** it will currently use lit 3.x and Umbraco is running on lit 2.8.0. so you need to change that in the package.json file.

```
cd assets
npm install
npm install --force --registry https://www.myget.org/F/umbracoprereleases/npm/ -D @umbraco-cms/backoffice@14.0.0--preview004
```

### vite.config.ts
My vite.config.ts file is slightly diffrent from umbraco's

```ts
import { defineConfig } from "vite";

export default defineConfig({
    build: {
        lib: {
            entry: "src/index.ts", // your web component source file
            formats: ["es"],
        },
        outDir: "../wwwroot/app_plugins/mypackage", 
        emptyOutDir: true,
        sourcemap: true,
        rollupOptions: {
            external: [/^@umbraco/],
            onwarn: () => { },
        },
    },
});
```

this is diffrent from the config in umbraco docs because it puts the out put into the `wwwroot` folder, so our built files become part of the razor class library. 

and this point we also clean up the vite template, we don't need the .svg and .css files. and we can remove myelement.ts, we are going to replace it with a index.ts 
as the entry point for our projects.

### umbraco-package.json

Now we need an `umbraco-package.json` file in our project so in our `assets/public` folder we create this one.  

```json
{
    "$schema": "../umbraco-package-schema.json",
    "name": "mypackage",
    "id": "mypackage",
    "version": "0.1.0",
    "allowTelemetry": true,
    "extensions": [
        {
            "name": "mypackage.entrypoint",
            "alias": "mypackage.EntryPoint",
            "type": "entryPoint",
            "js": "/app_plugins/mypackage/assets.js"
        }
    ]
}
```

Here we are going to have an 'entrypoint' extension which will point to a js file that does all the work.

we will then be defining all of our other extensions in typescript. In my opinion this is cleaner, gives you better intellisense, etc and just means you never have to worry about the umbraco-package.json file again.

> Now you will notice here the schema file will give you a warning because we don't have that yet.
> for the purposes of our build we can get it by building the webiste.
> ```
> dotnet build .\MyPackage.Website\
> ```
> this will give us an `umbraco-package-schema.json` which we can copy to the root of our assets folder. in the MyPackage.Client project.

### Entry point

As above we are going to have a typescript file do all the work of registering our extensions. 

so in the `src` folder we are creating `index.ts`

```js
import { UmbEntryPointOnInit } from '@umbraco-cms/backoffice/extension-api';

// load up the things here. 

export const onInit: UmbEntryPointOnInit = (_host, extensionRegistry) => {
    
    // register them here. 
    // extensionRegistry.registerMany(...)
};
```

## A Basic skeleton of a package
This is the basic skeleton, of a package layout, from here you can add extensions to the src folder - 

when built things will arrive in your `wwwroot/app_plugins` folder.

and because its all razor the website will get them and refresh. 

--- 

last tip now, consider adding a "watch" command to your package.json file

```
vite build --watch
```

With this in place when you change a .ts file in your assets/src folder, vite will rebuild the files, razor will detect the changes, and the umbraco back office will reload in your browser all without you having type/press anything else.