Skip to content

Commit

Permalink
feat: add standalone usage #3
Browse files Browse the repository at this point in the history
  • Loading branch information
rehanvandermerwe committed Jun 13, 2023
1 parent 26b9ff6 commit a965685
Show file tree
Hide file tree
Showing 8 changed files with 171 additions and 34 deletions.
43 changes: 34 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,35 @@
# Serverless Website Analytics Client

`/package` - Contains the NPM package code as published on [npm](https://www.npmjs.com/package/serverless-website-analytics-client)
and installed by clients.
`/usage` - Contains the basic `create` app for: Vue, React and Svelte that showcase how to use the client in each of
those frameworks. This is also highlighted in the next Usage section.

## Usage

There are **two ways to use the client**:
- **Standalone import script** - Single line, standard JS script in your HTML.
- **SDK client** - Import the SDK client into your project and use in any SPA.

### Standalone Import Script Usage

Then include the standalone script in your HTML:
```html
<html lang="en">
<head> ... </head>
<body>
...
<script src="<YOUR BACKEND ORIGIN>/cdn/client-script.js" site="<THE SITE YOU ARE TRACKING>"></script>
</body>
</html>
```

You need to replace `<YOUR BACKEND ORIGIN>` with the origin of your deployed backend. Available attributes on the script
are:
- `site` - Required. The name of your site, this must correspond with the name you specified when deploying the
`serverless-website-analytics` backend.
- `api-url` - Optional. Uses the same origin as the current script if not specified. This is the URL to the backend.
Allowing it to be specified opens a few use cases for testing.
- `serverless-website-analytics` - Optional. This is only required if the browser does not support `document.currentScript`
(All modern browsers since 2015 do). Only specify the tag, no value is needed.

### SDK Client Usage

Install the client:
```
npm install serverless-website-analytics-client
Expand All @@ -19,9 +42,9 @@ when deploying the `serverless-website-analytics` backend. You also need the URL
site's `Origin` is whitelisted in the backend config.
2. On each route change call the `analyticsPageChange` function with the name of the new page.

The following sections show you how to do it in various frameworks.
The following sections show you how to do it in a few frameworks, but you can still DIY with the SDK in **ANY framework**.

### Vue
#### Vue

[_./usage/vue/vue-project/src/main.ts_](https://github.com/rehanvdm/serverless-website-analytics-client/blob/master/usage/vue/vue-project/src/main.ts)
```typescript
Expand All @@ -44,7 +67,7 @@ router.afterEach((event) => {
app.mount('#app');
```

### React
#### React

[_./usage/react/react-project/src/main.tsx_](https://github.com/rehanvdm/serverless-website-analytics-client/blob/master/usage/react/react-project/src/main.tsx)
```typescript
Expand Down Expand Up @@ -74,7 +97,7 @@ ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
)
```

### Svelte
#### Svelte

[_./usage/svelte/svelte-project/src/App.svelte_](https://github.com/rehanvdm/serverless-website-analytics-client/blob/master/usage/svelte/svelte-project/src/App.svelte)
```sveltehtml
Expand Down Expand Up @@ -106,6 +129,8 @@ function routeLoaded(event) {
</script>
```

..Any other framework

## Package src

The src located at `package/src/index.ts` does not use any libraries to generate the API. The TypeScript types however
Expand Down
53 changes: 32 additions & 21 deletions package/scripts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ const paths = {
workingDir: path.resolve(__dirname),
src: path.resolve(__dirname+"/src"),
srcInput: path.resolve(__dirname+"/src/index.ts"),
srcInputCdn: path.resolve(__dirname+"/src/cdn/client-script.ts"),
openApiSpec: path.resolve(__dirname+"/src/OpenAPI-Ingest.yaml"),
dist: path.resolve(__dirname+"/dist"),
distOutputCjs: path.resolve(__dirname+"/dist/index.js"),
distOutputEsm: path.resolve(__dirname+"/dist/index.mjs"),
distCdn: path.resolve(__dirname+"/dist/cdn"),
}


Expand Down Expand Up @@ -78,24 +78,21 @@ export async function fileExists(path: string){
export async function folderExists(path: string){
return fs.opendir(path).then(async (dir) => { await dir.close(); return true; }).catch(err => false)
}
async function build()
{
console.time("* BUILD");

console.log("** Clean");
let packageFolderExist = await folderExists(paths.dist);
async function transpile(srcInput: string, dist: string, generateTypes: boolean) {
console.log("*** Clean: " +dist);
let packageFolderExist = await folderExists(dist);
if(!packageFolderExist) //create
await fs.mkdir(paths.dist,{ recursive: true });
await fs.mkdir(dist,{ recursive: true });
else //clear contents and recreate
{
await fs.rm(paths.dist,{ recursive: true });
await fs.mkdir(paths.dist,{ recursive: true });
await fs.rm(dist,{ recursive: true });
await fs.mkdir(dist,{ recursive: true });
}


console.log("** Bundling");
console.log("*** Transpiling: " +srcInput);
const bundle = await rollup({
input: paths.srcInput,
input: srcInput,
plugins: [
typescript({}),
nodeResolve({
Expand All @@ -104,17 +101,33 @@ async function build()
terser({sourceMap: true}),
]
});

const fileName = path.basename(srcInput, path.extname(srcInput));
const distOutputCjs = dist + "/"+fileName+".js";
const distOutputEsm = dist + "/"+fileName+".mjs";
//@ts-ignore because sourcemap is specified correctly https://github.com/terser/terser#source-map-options
await bundle.write({ file: paths.distOutputCjs, format: 'cjs', sourcemap: { filename: "out.js", url: "out.js.map"} });
await bundle.write({file: distOutputCjs, format: 'cjs', sourcemap: {filename: "out.js", url: "out.js.map"}});
//@ts-ignore because sourcemap is specified correctly https://github.com/terser/terser#source-map-options
await bundle.write({ file: paths.distOutputEsm, format: 'esm', sourcemap: { filename: "out.mjs", url: "out.mjs.map"} });
await bundle.write({file: distOutputEsm, format: 'esm', sourcemap: {filename: "out.mjs", url: "out.mjs.map"}});

if(generateTypes)
{
console.log("*** Generate types - d.ts");
await execaCommand("tsc --declaration --emitDeclarationOnly " + srcInput + " " +
"--outDir " + dist, {reject: true});
}
}

async function build()
{
console.time("* BUILD");
console.log("* BUILD");

console.log("** Generate types - d.ts");
await execaCommand("tsc --declaration --emitDeclarationOnly "+paths.srcInput+" " +
"--outDir "+paths.dist, { reject: true });
console.log("** Transpiling..");
await transpile(paths.srcInput, paths.dist, true,);
await transpile(paths.srcInputCdn, paths.distCdn, false);

console.log("** Coping files");
console.log("** Coping files..");
await fs.copyFile(paths.workingDir+"/package.json", paths.dist+"/package.json");

// Read the package.json that will be published and remove some stuff
Expand All @@ -124,10 +137,8 @@ async function build()
delete packageJson.wireit;
await fs.writeFile(paths.dist+"/package.json", JSON.stringify(packageJson, null, 2));


await fs.copyFile(paths.topLevelDir+"/README.md", paths.dist+"/README.md");


console.timeEnd("* BUILD");
}

63 changes: 63 additions & 0 deletions package/src/cdn/client-script.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import * as swaClient from "../";

(() => {
/* Only specify the data tag serverless-website-analytics if the browser does not support `document.currentScript`
* All modern browsers since 2015 do */
const me = document.currentScript || document.querySelector('script[serverless-website-analytics]');
if(!me)
throw new Error("Could not find script tag with attribute 'serverless-website-analytics' on the script tag.");

const site = me.getAttribute('site');
if(!site)
throw new Error("Could not find attribute 'site' on the script tag.");

/* The API URL to send the metrics to will 99% be the same as where the standalone script is loaded from, only use
* the `api-url` if specified explicitly. */
let scriptOrigin = "";
try {
//@ts-ignore
scriptOrigin = new URL(me.src).origin;
} catch (e) {
console.error("Could not parse URL from script tag", e);
}
let apiUrl = me.getAttribute('api-url');
if(!scriptOrigin && !apiUrl)
throw new Error("Could not auto-detect script origin, specify the 'api-url' attribute on the script tag.");
if(!apiUrl)
apiUrl = scriptOrigin;

const routing = me.getAttribute('routing')
if(routing && (routing !== "path" && routing !== "hash"))
throw new Error("Attribute 'routing' must be either 'path' or 'hash'");

function getPath() {
if(routing === "path")
return window.location.pathname;
else if(routing === "hash")
return window.location.hash ? window.location.hash : "/";
else
return window.location.pathname + (window.location.hash ? "#" + window.location.hash : "");
}

//@ts-ignore
window.swa = swaClient;
swaClient.v1.analyticsPageInit({
inBrowser: true,
site: site,
apiUrl: apiUrl,
// debug: true,
});

let currentPage = location.href;
setInterval(function() {
if (currentPage != location.href) {
currentPage = location.href;
// console.log('New URL:', getPath());
swaClient.v1.analyticsPageChange(getPath());
}
}, 500);

swaClient.v1.analyticsPageChange(getPath());

})();

4 changes: 0 additions & 4 deletions package/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,6 @@ export namespace v1
LOCAL: {
USER_ID: storagePrefix+"userId",
},
SESSION: {
CURRENT_PAGE_ANALYTIC: storagePrefix+"currentPageAnalytic",
PAGE_TIME_INCREMENT_STARTED: storagePrefix+"pageTimeIncrementStarted",
}
};

const pathTrackPage = "/api-ingest/v1/page/view";
Expand Down
19 changes: 19 additions & 0 deletions usage/standalone/about.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>About</title>
</head>
<body>
<h1>Test for Standalone - About</h1>

<br>
<br>

<p>Click for the <a href="index.html">Home</a> page</p>


<script src="<YOUR BACKEND ORIGIN>/cdn/client-script.js" site="<THE SITE YOU ARE TRACKING>"></script>
<!--<script src="../../package/dist/cdn/client-script.js" serverless-website-analytics site="my-project.com" api-url="http://localhost:5000"></script>-->
</body>
</html>
20 changes: 20 additions & 0 deletions usage/standalone/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Home</title>
</head>
<body>
<h1>Test for Standalone - Home</h1>

<br>
<br>

<p>Click for the <a href="about.html">About</a> page</p>
<p>Click for the <a href="index.html#test/something">Same with hash</a> page</p>

<script src="<YOUR BACKEND ORIGIN>/cdn/client-script.js" site="<THE SITE YOU ARE TRACKING>"></script>
<!--<script src="../../package/dist/cdn/client-script.js" serverless-website-analytics site="my-project.com" api-url="http://localhost:5000"></script>-->
<!--<script src="../../package/dist/cdn/client-script.js" site="my-project.com"></script>-->
</body>
</html>
2 changes: 2 additions & 0 deletions usage/vue/vue-project/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,7 @@
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
<!-- <script src="/cdn/client-script.js" serverless-website-analytics site="my-project.com" api-url="http://localhost:5000"></script>-->

</body>
</html>
1 change: 1 addition & 0 deletions usage/vue/vue-project/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"private": true,
"scripts": {
"dev": "vite",
"copy-cdn-file-for-test": "cp ./../../../package/dist/cdn/client-script.js ./public/cdn",
"build": "run-p type-check build-only",
"preview": "vite preview",
"build-only": "vite build",
Expand Down

0 comments on commit a965685

Please sign in to comment.