erayerdin

joined 1 year ago
 

So, I'm basically trying to parse a string literal with nom. This is the code I've come up with:

use nom::{
    bytes::complete::{tag, take_until},
    sequence::delimited,
    IResult,
};

/// Parses string literals.
fn parse_literal<'a>(input: &'a str) -> IResult<&'a str, &'a str> {
    // escape tag identifier is the same as delimiter, obviously
    let escape_tag_identifier =
        input
            .chars()
            .nth(0)
            .ok_or(nom::Err::Error(nom::error::Error::new(
                input,
                nom::error::ErrorKind::Verify,
            )))?;

    let (remaining, value) = delimited(
        tag(escape_tag_identifier.to_string().as_str()),
        take_until(match escape_tag_identifier {
            '\'' => "'",
            '"' => "\"",
            _ => unreachable!("parse_literal>>take_until branched into unreachable."),
        }),
        tag(escape_tag_identifier.to_string().as_str()),
    )(input)?;

    Ok((remaining, value))
}

#[cfg(test)]
mod literal_tests {
    use super::*;

    #[rstest]
    #[case(r#""foo""#, "foo")]
    #[case(r#""foo bar""#, "foo bar")]
    #[case(r#""foo \" bar""#, r#"foo " bar"#)]
    fn test_dquotes(#[case] input: &str, #[case] expected_output: &str) {
        let result = parse_literal(input);
        assert_eq!(result, Ok(("", expected_output)));
    }

    #[rstest]
    #[case("'foo'", "foo")]
    #[case("'foo bar'", "foo bar")]
    #[case(r#"'foo \' bar'"#, "foo ' bar")]
    fn test_squotes(#[case] input: &str, #[case] expected_output: &str) {
        let result = parse_literal(input);
        assert_eq!(result, Ok(("", expected_output)));
    }

    #[rstest]
    #[case(r#""foo'"#, "foo'")]
    #[case(r#"'foo""#, r#"foo""#)]
    fn test_errs(#[case] input: &str, #[case] expected_err_input: &str) {
        let result = parse_literal(input);
        assert_eq!(
            result,
            Err(nom::Err::Error(nom::error::Error::new(
                expected_err_input,
                nom::error::ErrorKind::TakeUntil
            ))),
        );
    }
}

Note: The example uses rstest for tests.

Although it looks a little bit complex, actually, it is not. Basically, the parse function is parse_literal. The tests are separated for double quotes and single quotes and errors.,

When you run the tests, you will realize first and second cases for single and double quotes run successfully. The problem is with the third case of each: #[case(r#""foo \" bar""#, r#"foo " bar"#)] for test_dquotes and #[case(r#"'foo \' bar'"#, "foo ' bar")] for test_squotes.

Ideally, if a string literal is defined with single quotes and has single quotes in its content, the single quotes can be escaped with single quotes again. Same goes for double quotes as well. To demonstrate in a pseudocode:

"foo ' bar" // is ok
"foo \" bar" // is ok
"foo " bar" // is err
'foo " bar' // is ok
'foo \' bar' // is ok
'foo ' bar' // is err

Currently, in the code, I take characters until the delimiter with take_until, which reaches to the end of the input, which, let's say, in this case, is guaranteed to contain only and only the string literal as input. So it's kind of okay for first and second cases in the tests.

But, of course, this fails in the third cases of each test since the input has the delimiter character early on, finishes early and returns the remaining.

This is only for research purposes, so you do not need to give a fully-featured answer. A pathway is, as well, appreciated.

Thanks in advance.

 

I have a function as such:

export type SendMessageParams = {
  chatSession?: ChatSession,
  // ... other params ...
};

const sendMessage = async ({
  chatSession,
  // ... other params ...
}: SendMessageParams): Promise<void> => {
  // await chatSession?.sendMessage()
  // somewhere in implementation
};

export default sendMessage;

ChatSession is from @google/generative-ai.

I'd like to mock it in my test file as such:

let defaultParams: SendMessageParams;

beforeEach(() => {
  jest.mock('@google/generative-ai', () => ({
    ChatSession: {
      sendMessage: async (content: string) => content,
    },
  }));
  defaultParams = {
    chatSession: new ChatSession('', ''),
    // ... other params ...
  };
});

afterEach(() => {
  jest.clearAllMocks();
});

it('should send message', async () => {
  // await sendMessage();
});

When I run npm run test, I get the error saying:

 FAIL  tests/logic/actions/sendMessage.test.ts
  ● should send message

    ReferenceError: fetch is not defined

      43 |   const sendMessageInner = async (messages: Message[]) => {
      44 |     setMessageListState(messages);
    > 45 |     const result = await chatSession?.sendMessage(content);
         |                    ^
      46 |     const responseText = result?.response.text();
      47 |     if (responseText) {
      48 |       const responseMessage: Message = {

      at makeRequest (node_modules/@google/generative-ai/dist/index.js:246:9)
      at generateContent (node_modules/@google/generative-ai/dist/index.js:655:28)
      at node_modules/@google/generative-ai/dist/index.js:890:25
      at ChatSession.sendMessage (node_modules/@google/generative-ai/dist/index.js:909:9)
      at sendMessageInner (src/logic/actions/sendMessage.ts:45:20)
      at src/logic/actions/sendMessage.ts:72:7
      at sendMessage (src/logic/actions/sendMessage.ts:59:3)
      at Object.<anonymous> (tests/logic/actions/sendMessage.test.ts:44:3)

...which hints that chatSession.sendMessage method still uses the real implementation instead of mock.

I'd like to know why this happens and what the solution would be.

Thanks in advance.


Environment

  • Node 20.11.0 (lts/iron)
  • Jest 29.7.0
  • @google/generative-ai 0.5.0 (if relevant)
 

If you're a library developer for Javascript or Typescript, a new badge to show unpacked size of your packages is now available.

Badge URL format:

https://img.shields.io/npm/unpacked-size/npmPackageName
 

I've been writing a hook+component library for React and Firebase, the source of which can be seen here.

It reached to a certain stability. My current goal is to reduce the size as much as possible. I've checked documents and stuff and this is the vite.config.ts that I have:

import peerDepsExternal from "rollup-plugin-peer-deps-external";
import { defineConfig } from "vite";
import dts from "vite-plugin-dts";
import { peerDependencies } from "./package.json";

export default defineConfig({
  build: {
    lib: {
      entry: "./src/index.ts", // Specifies the entry point for building the library.
      name: "firereact", // Sets the name of the generated library.
      fileName: (format) => `index.${format}.js`, // Generates the output file name based on the format.
      formats: ["cjs", "es"], // Specifies the output formats (CommonJS and ES modules).
    },
    rollupOptions: {
      external: [...Object.keys(peerDependencies)], // Defines external dependencies for Rollup bundling.
    },
    sourcemap: true, // Generates source maps for debugging.
    emptyOutDir: true, // Clears the output directory before building.
  },
  plugins: [dts(), peerDepsExternal()], // Uses the 'vite-plugin-dts' plugin for generating TypeScript declaration files (d.ts).
});

build.lib.rollupOptions.external is to be expected: It gets peer dependencies from package.json. peerDepsExternal helps to not bundle the dependencies of peer dependencies.

Still, I've thought the final size is not that reasonable compared to some similar libraries.

That's why I've checked what's being included with npx vite-bundle-visualizer.

Seems like @firebase is being included to the final library. I can also confirm it with less dist/index.es.js and see @firebase being mangled.

Firebase is a peer dependency so it shouldn't be bundled to the final library (or maybe I misunderstand something cruicial about peerDependencies).

How do I exclude @firebase from the library?


Troubleshooting

Here are some ways that I've tried to remove @firebase from the final library:

  • Tried to manually add it into build.lib.rollupOptions.external in vite.config.ts.
  • Played with some settings in tsconfig.json.
 

I've decided to write my first library in Typescript, which is here.

I've got some questions that I don't think is suitable for StackOverflow because it's quite case-specific rather than being generic and I've got a couple of them rather than one.

I'm trying to wrap my head around JS/TS module system for some while. There are some problems with my library:

  1. If a user imports a hook, they have to do import { useDocument } from 'firereact/firestore/useDocument', but it'd be much better if they could do import { useDocument } from 'firereact/firestore'. I've tried many ways but I couldn't export it to firestore/index.ts I guess. What am I doing wrong?
  2. I have realized that consumers can also import test modules and firebase.ts, which are only used for testing and it is not desirable for them to be imported by the consumers. How can I ignore some specific exports while bundling? They are meant to be used internally.

Thanks in advance. And btw, extra reviews and critics are appreciated since this is going to be my first library.

 

I know this is kind of a soft problem and is highly dependent on the context of a project.

For my own projects, I use scrum points to estimate how much effort it will require to reach to a certain point. I measured my throughput and it seems like, with the amount of time left from my daily job, I can complete around 100 points every month.

Recently, this idea is stuck in my head. Every (web/mobile) project requires a certain set of requirements such as:

  • Authentication and authorization
  • Performance metrics and logging
  • Storage
  • CRUD for each model etc.

Of course, mostly through Firebase.

I was wondering if there's a way, a website or a platform maybe, to define a list of features/stories and get an estimated total points.